plug.vim 82 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863
  1. " vim-plug: Vim plugin manager
  2. " ============================
  3. "
  4. " 1. Download plug.vim and put it in 'autoload' directory
  5. "
  6. " # Vim
  7. " curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
  8. " https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
  9. "
  10. " # Neovim
  11. " sh -c 'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs \
  12. " https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'
  13. "
  14. " 2. Add a vim-plug section to your ~/.vimrc (or ~/.config/nvim/init.vim for Neovim)
  15. "
  16. " call plug#begin()
  17. "
  18. " " List your plugins here
  19. " Plug 'tpope/vim-sensible'
  20. "
  21. " call plug#end()
  22. "
  23. " 3. Reload the file or restart Vim, then you can,
  24. "
  25. " :PlugInstall to install plugins
  26. " :PlugUpdate to update plugins
  27. " :PlugDiff to review the changes from the last update
  28. " :PlugClean to remove plugins no longer in the list
  29. "
  30. " For more information, see https://github.com/junegunn/vim-plug
  31. "
  32. "
  33. " Copyright (c) 2024 Junegunn Choi
  34. "
  35. " MIT License
  36. "
  37. " Permission is hereby granted, free of charge, to any person obtaining
  38. " a copy of this software and associated documentation files (the
  39. " "Software"), to deal in the Software without restriction, including
  40. " without limitation the rights to use, copy, modify, merge, publish,
  41. " distribute, sublicense, and/or sell copies of the Software, and to
  42. " permit persons to whom the Software is furnished to do so, subject to
  43. " the following conditions:
  44. "
  45. " The above copyright notice and this permission notice shall be
  46. " included in all copies or substantial portions of the Software.
  47. "
  48. " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  49. " EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  50. " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  51. " NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  52. " LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  53. " OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  54. " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  55. if exists('g:loaded_plug')
  56. finish
  57. endif
  58. let g:loaded_plug = 1
  59. let s:cpo_save = &cpo
  60. set cpo&vim
  61. let s:plug_src = 'https://github.com/junegunn/vim-plug.git'
  62. let s:plug_tab = get(s:, 'plug_tab', -1)
  63. let s:plug_buf = get(s:, 'plug_buf', -1)
  64. let s:mac_gui = has('gui_macvim') && has('gui_running')
  65. let s:is_win = has('win32')
  66. let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win)
  67. let s:vim8 = has('patch-8.0.0039') && exists('*job_start')
  68. if s:is_win && &shellslash
  69. set noshellslash
  70. let s:me = resolve(expand('<sfile>:p'))
  71. set shellslash
  72. else
  73. let s:me = resolve(expand('<sfile>:p'))
  74. endif
  75. let s:base_spec = { 'branch': '', '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! s:is_powershell(shell)
  85. return a:shell =~# 'powershell\(\.exe\)\?$' || a:shell =~# 'pwsh\(\.exe\)\?$'
  86. endfunction
  87. function! s:isabsolute(dir) abort
  88. return a:dir =~# '^/' || (has('win32') && a:dir =~? '^\%(\\\|[A-Z]:\)')
  89. endfunction
  90. function! s:git_dir(dir) abort
  91. let gitdir = s:trim(a:dir) . '/.git'
  92. if isdirectory(gitdir)
  93. return gitdir
  94. endif
  95. if !filereadable(gitdir)
  96. return ''
  97. endif
  98. let gitdir = matchstr(get(readfile(gitdir), 0, ''), '^gitdir: \zs.*')
  99. if len(gitdir) && !s:isabsolute(gitdir)
  100. let gitdir = a:dir . '/' . gitdir
  101. endif
  102. return isdirectory(gitdir) ? gitdir : ''
  103. endfunction
  104. function! s:git_origin_url(dir) abort
  105. let gitdir = s:git_dir(a:dir)
  106. let config = gitdir . '/config'
  107. if empty(gitdir) || !filereadable(config)
  108. return ''
  109. endif
  110. return matchstr(join(readfile(config)), '\[remote "origin"\].\{-}url\s*=\s*\zs\S*\ze')
  111. endfunction
  112. function! s:git_revision(dir) abort
  113. let gitdir = s:git_dir(a:dir)
  114. let head = gitdir . '/HEAD'
  115. if empty(gitdir) || !filereadable(head)
  116. return ''
  117. endif
  118. let line = get(readfile(head), 0, '')
  119. let ref = matchstr(line, '^ref: \zs.*')
  120. if empty(ref)
  121. return line
  122. endif
  123. if filereadable(gitdir . '/' . ref)
  124. return get(readfile(gitdir . '/' . ref), 0, '')
  125. endif
  126. if filereadable(gitdir . '/packed-refs')
  127. for line in readfile(gitdir . '/packed-refs')
  128. if line =~# ' ' . ref
  129. return matchstr(line, '^[0-9a-f]*')
  130. endif
  131. endfor
  132. endif
  133. return ''
  134. endfunction
  135. function! s:git_local_branch(dir) abort
  136. let gitdir = s:git_dir(a:dir)
  137. let head = gitdir . '/HEAD'
  138. if empty(gitdir) || !filereadable(head)
  139. return ''
  140. endif
  141. let branch = matchstr(get(readfile(head), 0, ''), '^ref: refs/heads/\zs.*')
  142. return len(branch) ? branch : 'HEAD'
  143. endfunction
  144. function! s:git_origin_branch(spec)
  145. if len(a:spec.branch)
  146. return a:spec.branch
  147. endif
  148. " The file may not be present if this is a local repository
  149. let gitdir = s:git_dir(a:spec.dir)
  150. let origin_head = gitdir.'/refs/remotes/origin/HEAD'
  151. if len(gitdir) && filereadable(origin_head)
  152. return matchstr(get(readfile(origin_head), 0, ''),
  153. \ '^ref: refs/remotes/origin/\zs.*')
  154. endif
  155. " The command may not return the name of a branch in detached HEAD state
  156. let result = s:lines(s:system('git symbolic-ref --short HEAD', a:spec.dir))
  157. return v:shell_error ? '' : result[-1]
  158. endfunction
  159. if s:is_win
  160. function! s:plug_call(fn, ...)
  161. let shellslash = &shellslash
  162. try
  163. set noshellslash
  164. return call(a:fn, a:000)
  165. finally
  166. let &shellslash = shellslash
  167. endtry
  168. endfunction
  169. else
  170. function! s:plug_call(fn, ...)
  171. return call(a:fn, a:000)
  172. endfunction
  173. endif
  174. function! s:plug_getcwd()
  175. return s:plug_call('getcwd')
  176. endfunction
  177. function! s:plug_fnamemodify(fname, mods)
  178. return s:plug_call('fnamemodify', a:fname, a:mods)
  179. endfunction
  180. function! s:plug_expand(fmt)
  181. return s:plug_call('expand', a:fmt, 1)
  182. endfunction
  183. function! s:plug_tempname()
  184. return s:plug_call('tempname')
  185. endfunction
  186. function! plug#begin(...)
  187. if a:0 > 0
  188. let home = s:path(s:plug_fnamemodify(s:plug_expand(a:1), ':p'))
  189. elseif exists('g:plug_home')
  190. let home = s:path(g:plug_home)
  191. elseif has('nvim')
  192. let home = stdpath('data') . '/plugged'
  193. elseif !empty(&rtp)
  194. let home = s:path(split(&rtp, ',')[0]) . '/plugged'
  195. else
  196. return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.')
  197. endif
  198. if s:plug_fnamemodify(home, ':t') ==# 'plugin' && s:plug_fnamemodify(home, ':h') ==# s:first_rtp
  199. return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.')
  200. endif
  201. let g:plug_home = home
  202. let g:plugs = {}
  203. let g:plugs_order = []
  204. let s:triggers = {}
  205. call s:define_commands()
  206. return 1
  207. endfunction
  208. function! s:define_commands()
  209. command! -nargs=+ -bar Plug call plug#(<args>)
  210. if !executable('git')
  211. return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.')
  212. endif
  213. if has('win32')
  214. \ && &shellslash
  215. \ && (&shell =~# 'cmd\(\.exe\)\?$' || s:is_powershell(&shell))
  216. return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.')
  217. endif
  218. if !has('nvim')
  219. \ && (has('win32') || has('win32unix'))
  220. \ && !has('multi_byte')
  221. return s:err('Vim needs +multi_byte feature on Windows to run shell commands. Enable +iconv for best results.')
  222. endif
  223. command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(<bang>0, [<f-args>])
  224. command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update(<bang>0, [<f-args>])
  225. command! -nargs=0 -bar -bang PlugClean call s:clean(<bang>0)
  226. command! -nargs=0 -bar PlugUpgrade if s:upgrade() | execute 'source' s:esc(s:me) | endif
  227. command! -nargs=0 -bar PlugStatus call s:status()
  228. command! -nargs=0 -bar PlugDiff call s:diff()
  229. command! -nargs=? -bar -bang -complete=file PlugSnapshot call s:snapshot(<bang>0, <f-args>)
  230. endfunction
  231. function! s:to_a(v)
  232. return type(a:v) == s:TYPE.list ? a:v : [a:v]
  233. endfunction
  234. function! s:to_s(v)
  235. return type(a:v) == s:TYPE.string ? a:v : join(a:v, "\n") . "\n"
  236. endfunction
  237. function! s:glob(from, pattern)
  238. return s:lines(globpath(a:from, a:pattern))
  239. endfunction
  240. function! s:source(from, ...)
  241. let found = 0
  242. for pattern in a:000
  243. for vim in s:glob(a:from, pattern)
  244. execute 'source' s:esc(vim)
  245. let found = 1
  246. endfor
  247. endfor
  248. return found
  249. endfunction
  250. function! s:assoc(dict, key, val)
  251. let a:dict[a:key] = add(get(a:dict, a:key, []), a:val)
  252. endfunction
  253. function! s:ask(message, ...)
  254. call inputsave()
  255. echohl WarningMsg
  256. let answer = input(a:message.(a:0 ? ' (y/N/a) ' : ' (y/N) '))
  257. echohl None
  258. call inputrestore()
  259. echo "\r"
  260. return (a:0 && answer =~? '^a') ? 2 : (answer =~? '^y') ? 1 : 0
  261. endfunction
  262. function! s:ask_no_interrupt(...)
  263. try
  264. return call('s:ask', a:000)
  265. catch
  266. return 0
  267. endtry
  268. endfunction
  269. function! s:lazy(plug, opt)
  270. return has_key(a:plug, a:opt) &&
  271. \ (empty(s:to_a(a:plug[a:opt])) ||
  272. \ !isdirectory(a:plug.dir) ||
  273. \ len(s:glob(s:rtp(a:plug), 'plugin')) ||
  274. \ len(s:glob(s:rtp(a:plug), 'after/plugin')))
  275. endfunction
  276. function! plug#end()
  277. if !exists('g:plugs')
  278. return s:err('plug#end() called without calling plug#begin() first')
  279. endif
  280. if exists('#PlugLOD')
  281. augroup PlugLOD
  282. autocmd!
  283. augroup END
  284. augroup! PlugLOD
  285. endif
  286. let lod = { 'ft': {}, 'map': {}, 'cmd': {} }
  287. if get(g:, 'did_load_filetypes', 0)
  288. filetype off
  289. endif
  290. for name in g:plugs_order
  291. if !has_key(g:plugs, name)
  292. continue
  293. endif
  294. let plug = g:plugs[name]
  295. if get(s:loaded, name, 0) || !s:lazy(plug, 'on') && !s:lazy(plug, 'for')
  296. let s:loaded[name] = 1
  297. continue
  298. endif
  299. if has_key(plug, 'on')
  300. let s:triggers[name] = { 'map': [], 'cmd': [] }
  301. for cmd in s:to_a(plug.on)
  302. if cmd =~? '^<Plug>.\+'
  303. if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i'))
  304. call s:assoc(lod.map, cmd, name)
  305. endif
  306. call add(s:triggers[name].map, cmd)
  307. elseif cmd =~# '^[A-Z]'
  308. let cmd = substitute(cmd, '!*$', '', '')
  309. if exists(':'.cmd) != 2
  310. call s:assoc(lod.cmd, cmd, name)
  311. endif
  312. call add(s:triggers[name].cmd, cmd)
  313. else
  314. call s:err('Invalid `on` option: '.cmd.
  315. \ '. Should start with an uppercase letter or `<Plug>`.')
  316. endif
  317. endfor
  318. endif
  319. if has_key(plug, 'for')
  320. let types = s:to_a(plug.for)
  321. if !empty(types)
  322. augroup filetypedetect
  323. call s:source(s:rtp(plug), 'ftdetect/**/*.vim', 'after/ftdetect/**/*.vim')
  324. if has('nvim-0.5.0')
  325. call s:source(s:rtp(plug), 'ftdetect/**/*.lua', 'after/ftdetect/**/*.lua')
  326. endif
  327. augroup END
  328. endif
  329. for type in types
  330. call s:assoc(lod.ft, type, name)
  331. endfor
  332. endif
  333. endfor
  334. for [cmd, names] in items(lod.cmd)
  335. execute printf(
  336. \ 'command! -nargs=* -range -bang -complete=file %s call s:lod_cmd(%s, "<bang>", <line1>, <line2>, <q-args>, %s)',
  337. \ cmd, string(cmd), string(names))
  338. endfor
  339. for [map, names] in items(lod.map)
  340. for [mode, map_prefix, key_prefix] in
  341. \ [['i', '<C-\><C-O>', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']]
  342. execute printf(
  343. \ '%snoremap <silent> %s %s:<C-U>call <SID>lod_map(%s, %s, %s, "%s")<CR>',
  344. \ mode, map, map_prefix, string(map), string(names), mode != 'i', key_prefix)
  345. endfor
  346. endfor
  347. for [ft, names] in items(lod.ft)
  348. augroup PlugLOD
  349. execute printf('autocmd FileType %s call <SID>lod_ft(%s, %s)',
  350. \ ft, string(ft), string(names))
  351. augroup END
  352. endfor
  353. call s:reorg_rtp()
  354. filetype plugin indent on
  355. if has('vim_starting')
  356. if has('syntax') && !exists('g:syntax_on')
  357. syntax enable
  358. end
  359. else
  360. call s:reload_plugins()
  361. endif
  362. endfunction
  363. function! s:loaded_names()
  364. return filter(copy(g:plugs_order), 'get(s:loaded, v:val, 0)')
  365. endfunction
  366. function! s:load_plugin(spec)
  367. call s:source(s:rtp(a:spec), 'plugin/**/*.vim', 'after/plugin/**/*.vim')
  368. if has('nvim-0.5.0')
  369. call s:source(s:rtp(a:spec), 'plugin/**/*.lua', 'after/plugin/**/*.lua')
  370. endif
  371. endfunction
  372. function! s:reload_plugins()
  373. for name in s:loaded_names()
  374. call s:load_plugin(g:plugs[name])
  375. endfor
  376. endfunction
  377. function! s:trim(str)
  378. return substitute(a:str, '[\/]\+$', '', '')
  379. endfunction
  380. function! s:version_requirement(val, min)
  381. for idx in range(0, len(a:min) - 1)
  382. let v = get(a:val, idx, 0)
  383. if v < a:min[idx] | return 0
  384. elseif v > a:min[idx] | return 1
  385. endif
  386. endfor
  387. return 1
  388. endfunction
  389. function! s:git_version_requirement(...)
  390. if !exists('s:git_version')
  391. let s:git_version = map(split(split(s:system(['git', '--version']))[2], '\.'), 'str2nr(v:val)')
  392. endif
  393. return s:version_requirement(s:git_version, a:000)
  394. endfunction
  395. function! s:progress_opt(base)
  396. return a:base && !s:is_win &&
  397. \ s:git_version_requirement(1, 7, 1) ? '--progress' : ''
  398. endfunction
  399. function! s:rtp(spec)
  400. return s:path(a:spec.dir . get(a:spec, 'rtp', ''))
  401. endfunction
  402. if s:is_win
  403. function! s:path(path)
  404. return s:trim(substitute(a:path, '/', '\', 'g'))
  405. endfunction
  406. function! s:dirpath(path)
  407. return s:path(a:path) . '\'
  408. endfunction
  409. function! s:is_local_plug(repo)
  410. return a:repo =~? '^[a-z]:\|^[%~]'
  411. endfunction
  412. " Copied from fzf
  413. function! s:wrap_cmds(cmds)
  414. let cmds = [
  415. \ '@echo off',
  416. \ 'setlocal enabledelayedexpansion']
  417. \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds])
  418. \ + ['endlocal']
  419. if has('iconv')
  420. if !exists('s:codepage')
  421. let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0)
  422. endif
  423. return map(cmds, printf('iconv(v:val."\r", "%s", "cp%d")', &encoding, s:codepage))
  424. endif
  425. return map(cmds, 'v:val."\r"')
  426. endfunction
  427. function! s:batchfile(cmd)
  428. let batchfile = s:plug_tempname().'.bat'
  429. call writefile(s:wrap_cmds(a:cmd), batchfile)
  430. let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0})
  431. if s:is_powershell(&shell)
  432. let cmd = '& ' . cmd
  433. endif
  434. return [batchfile, cmd]
  435. endfunction
  436. else
  437. function! s:path(path)
  438. return s:trim(a:path)
  439. endfunction
  440. function! s:dirpath(path)
  441. return substitute(a:path, '[/\\]*$', '/', '')
  442. endfunction
  443. function! s:is_local_plug(repo)
  444. return a:repo[0] =~ '[/$~]'
  445. endfunction
  446. endif
  447. function! s:err(msg)
  448. echohl ErrorMsg
  449. echom '[vim-plug] '.a:msg
  450. echohl None
  451. endfunction
  452. function! s:warn(cmd, msg)
  453. echohl WarningMsg
  454. execute a:cmd 'a:msg'
  455. echohl None
  456. endfunction
  457. function! s:esc(path)
  458. return escape(a:path, ' ')
  459. endfunction
  460. function! s:escrtp(path)
  461. return escape(a:path, ' ,')
  462. endfunction
  463. function! s:remove_rtp()
  464. for name in s:loaded_names()
  465. let rtp = s:rtp(g:plugs[name])
  466. execute 'set rtp-='.s:escrtp(rtp)
  467. let after = globpath(rtp, 'after')
  468. if isdirectory(after)
  469. execute 'set rtp-='.s:escrtp(after)
  470. endif
  471. endfor
  472. endfunction
  473. function! s:reorg_rtp()
  474. if !empty(s:first_rtp)
  475. execute 'set rtp-='.s:first_rtp
  476. execute 'set rtp-='.s:last_rtp
  477. endif
  478. " &rtp is modified from outside
  479. if exists('s:prtp') && s:prtp !=# &rtp
  480. call s:remove_rtp()
  481. unlet! s:middle
  482. endif
  483. let s:middle = get(s:, 'middle', &rtp)
  484. let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])')
  485. let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), '!empty(v:val)')
  486. let rtp = join(map(rtps, 'escape(v:val, ",")'), ',')
  487. \ . ','.s:middle.','
  488. \ . join(map(afters, 'escape(v:val, ",")'), ',')
  489. let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g')
  490. let s:prtp = &rtp
  491. if !empty(s:first_rtp)
  492. execute 'set rtp^='.s:first_rtp
  493. execute 'set rtp+='.s:last_rtp
  494. endif
  495. endfunction
  496. function! s:doautocmd(...)
  497. if exists('#'.join(a:000, '#'))
  498. execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '<nomodeline>' : '') join(a:000)
  499. endif
  500. endfunction
  501. function! s:dobufread(names)
  502. for name in a:names
  503. let path = s:rtp(g:plugs[name])
  504. for dir in ['ftdetect', 'ftplugin', 'after/ftdetect', 'after/ftplugin']
  505. if len(finddir(dir, path))
  506. if exists('#BufRead')
  507. doautocmd BufRead
  508. endif
  509. return
  510. endif
  511. endfor
  512. endfor
  513. endfunction
  514. function! plug#load(...)
  515. if a:0 == 0
  516. return s:err('Argument missing: plugin name(s) required')
  517. endif
  518. if !exists('g:plugs')
  519. return s:err('plug#begin was not called')
  520. endif
  521. let names = a:0 == 1 && type(a:1) == s:TYPE.list ? a:1 : a:000
  522. let unknowns = filter(copy(names), '!has_key(g:plugs, v:val)')
  523. if !empty(unknowns)
  524. let s = len(unknowns) > 1 ? 's' : ''
  525. return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', ')))
  526. end
  527. let unloaded = filter(copy(names), '!get(s:loaded, v:val, 0)')
  528. if !empty(unloaded)
  529. for name in unloaded
  530. call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
  531. endfor
  532. call s:dobufread(unloaded)
  533. return 1
  534. end
  535. return 0
  536. endfunction
  537. function! s:remove_triggers(name)
  538. if !has_key(s:triggers, a:name)
  539. return
  540. endif
  541. for cmd in s:triggers[a:name].cmd
  542. execute 'silent! delc' cmd
  543. endfor
  544. for map in s:triggers[a:name].map
  545. execute 'silent! unmap' map
  546. execute 'silent! iunmap' map
  547. endfor
  548. call remove(s:triggers, a:name)
  549. endfunction
  550. function! s:lod(names, types, ...)
  551. for name in a:names
  552. call s:remove_triggers(name)
  553. let s:loaded[name] = 1
  554. endfor
  555. call s:reorg_rtp()
  556. for name in a:names
  557. let rtp = s:rtp(g:plugs[name])
  558. for dir in a:types
  559. call s:source(rtp, dir.'/**/*.vim')
  560. if has('nvim-0.5.0') " see neovim#14686
  561. call s:source(rtp, dir.'/**/*.lua')
  562. endif
  563. endfor
  564. if a:0
  565. if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2))
  566. execute 'runtime' a:1
  567. endif
  568. call s:source(rtp, a:2)
  569. endif
  570. call s:doautocmd('User', name)
  571. endfor
  572. endfunction
  573. function! s:lod_ft(pat, names)
  574. let syn = 'syntax/'.a:pat.'.vim'
  575. call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn)
  576. execute 'autocmd! PlugLOD FileType' a:pat
  577. call s:doautocmd('filetypeplugin', 'FileType')
  578. call s:doautocmd('filetypeindent', 'FileType')
  579. endfunction
  580. function! s:lod_cmd(cmd, bang, l1, l2, args, names)
  581. call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
  582. call s:dobufread(a:names)
  583. execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args)
  584. endfunction
  585. function! s:lod_map(map, names, with_prefix, prefix)
  586. call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
  587. call s:dobufread(a:names)
  588. let extra = ''
  589. while 1
  590. let c = getchar(0)
  591. if c == 0
  592. break
  593. endif
  594. let extra .= nr2char(c)
  595. endwhile
  596. if a:with_prefix
  597. let prefix = v:count ? v:count : ''
  598. let prefix .= '"'.v:register.a:prefix
  599. if mode(1) == 'no'
  600. if v:operator == 'c'
  601. let prefix = "\<esc>" . prefix
  602. endif
  603. let prefix .= v:operator
  604. endif
  605. call feedkeys(prefix, 'n')
  606. endif
  607. call feedkeys(substitute(a:map, '^<Plug>', "\<Plug>", '') . extra)
  608. endfunction
  609. function! plug#(repo, ...)
  610. if a:0 > 1
  611. return s:err('Invalid number of arguments (1..2)')
  612. endif
  613. try
  614. let repo = s:trim(a:repo)
  615. let opts = a:0 == 1 ? s:parse_options(a:1) : s:base_spec
  616. let name = get(opts, 'as', s:plug_fnamemodify(repo, ':t:s?\.git$??'))
  617. let spec = extend(s:infer_properties(name, repo), opts)
  618. if !has_key(g:plugs, name)
  619. call add(g:plugs_order, name)
  620. endif
  621. let g:plugs[name] = spec
  622. let s:loaded[name] = get(s:loaded, name, 0)
  623. catch
  624. return s:err(repo . ' ' . v:exception)
  625. endtry
  626. endfunction
  627. function! s:parse_options(arg)
  628. let opts = copy(s:base_spec)
  629. let type = type(a:arg)
  630. let opt_errfmt = 'Invalid argument for "%s" option of :Plug (expected: %s)'
  631. if type == s:TYPE.string
  632. if empty(a:arg)
  633. throw printf(opt_errfmt, 'tag', 'string')
  634. endif
  635. let opts.tag = a:arg
  636. elseif type == s:TYPE.dict
  637. for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as']
  638. if has_key(a:arg, opt)
  639. \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt]))
  640. throw printf(opt_errfmt, opt, 'string')
  641. endif
  642. endfor
  643. for opt in ['on', 'for']
  644. if has_key(a:arg, opt)
  645. \ && type(a:arg[opt]) != s:TYPE.list
  646. \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt]))
  647. throw printf(opt_errfmt, opt, 'string or list')
  648. endif
  649. endfor
  650. if has_key(a:arg, 'do')
  651. \ && type(a:arg.do) != s:TYPE.funcref
  652. \ && (type(a:arg.do) != s:TYPE.string || empty(a:arg.do))
  653. throw printf(opt_errfmt, 'do', 'string or funcref')
  654. endif
  655. call extend(opts, a:arg)
  656. if has_key(opts, 'dir')
  657. let opts.dir = s:dirpath(s:plug_expand(opts.dir))
  658. endif
  659. else
  660. throw 'Invalid argument type (expected: string or dictionary)'
  661. endif
  662. return opts
  663. endfunction
  664. function! s:infer_properties(name, repo)
  665. let repo = a:repo
  666. if s:is_local_plug(repo)
  667. return { 'dir': s:dirpath(s:plug_expand(repo)) }
  668. else
  669. if repo =~ ':'
  670. let uri = repo
  671. else
  672. if repo !~ '/'
  673. throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo)
  674. endif
  675. let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git')
  676. let uri = printf(fmt, repo)
  677. endif
  678. return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri }
  679. endif
  680. endfunction
  681. function! s:install(force, names)
  682. call s:update_impl(0, a:force, a:names)
  683. endfunction
  684. function! s:update(force, names)
  685. call s:update_impl(1, a:force, a:names)
  686. endfunction
  687. function! plug#helptags()
  688. if !exists('g:plugs')
  689. return s:err('plug#begin was not called')
  690. endif
  691. for spec in values(g:plugs)
  692. let docd = join([s:rtp(spec), 'doc'], '/')
  693. if isdirectory(docd)
  694. silent! execute 'helptags' s:esc(docd)
  695. endif
  696. endfor
  697. return 1
  698. endfunction
  699. function! s:syntax()
  700. syntax clear
  701. syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber
  702. syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX,plugAbort
  703. syn match plugNumber /[0-9]\+[0-9.]*/ contained
  704. syn match plugBracket /[[\]]/ contained
  705. syn match plugX /x/ contained
  706. syn match plugAbort /\~/ contained
  707. syn match plugDash /^-\{1}\ /
  708. syn match plugPlus /^+/
  709. syn match plugStar /^*/
  710. syn match plugMessage /\(^- \)\@<=.*/
  711. syn match plugName /\(^- \)\@<=[^ ]*:/
  712. syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/
  713. syn match plugTag /(tag: [^)]\+)/
  714. syn match plugInstall /\(^+ \)\@<=[^:]*/
  715. syn match plugUpdate /\(^* \)\@<=[^:]*/
  716. syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag
  717. syn match plugEdge /^ \X\+$/
  718. syn match plugEdge /^ \X*/ contained nextgroup=plugSha
  719. syn match plugSha /[0-9a-f]\{7,9}/ contained
  720. syn match plugRelDate /([^)]*)$/ contained
  721. syn match plugNotLoaded /(not loaded)$/
  722. syn match plugError /^x.*/
  723. syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/
  724. syn match plugH2 /^.*:\n-\+$/
  725. syn match plugH2 /^-\{2,}/
  726. syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
  727. hi def link plug1 Title
  728. hi def link plug2 Repeat
  729. hi def link plugH2 Type
  730. hi def link plugX Exception
  731. hi def link plugAbort Ignore
  732. hi def link plugBracket Structure
  733. hi def link plugNumber Number
  734. hi def link plugDash Special
  735. hi def link plugPlus Constant
  736. hi def link plugStar Boolean
  737. hi def link plugMessage Function
  738. hi def link plugName Label
  739. hi def link plugInstall Function
  740. hi def link plugUpdate Type
  741. hi def link plugError Error
  742. hi def link plugDeleted Ignore
  743. hi def link plugRelDate Comment
  744. hi def link plugEdge PreProc
  745. hi def link plugSha Identifier
  746. hi def link plugTag Constant
  747. hi def link plugNotLoaded Comment
  748. endfunction
  749. function! s:lpad(str, len)
  750. return a:str . repeat(' ', a:len - len(a:str))
  751. endfunction
  752. function! s:lines(msg)
  753. return split(a:msg, "[\r\n]")
  754. endfunction
  755. function! s:lastline(msg)
  756. return get(s:lines(a:msg), -1, '')
  757. endfunction
  758. function! s:new_window()
  759. execute get(g:, 'plug_window', '-tabnew')
  760. endfunction
  761. function! s:plug_window_exists()
  762. let buflist = tabpagebuflist(s:plug_tab)
  763. return !empty(buflist) && index(buflist, s:plug_buf) >= 0
  764. endfunction
  765. function! s:switch_in()
  766. if !s:plug_window_exists()
  767. return 0
  768. endif
  769. if winbufnr(0) != s:plug_buf
  770. let s:pos = [tabpagenr(), winnr(), winsaveview()]
  771. execute 'normal!' s:plug_tab.'gt'
  772. let winnr = bufwinnr(s:plug_buf)
  773. execute winnr.'wincmd w'
  774. call add(s:pos, winsaveview())
  775. else
  776. let s:pos = [winsaveview()]
  777. endif
  778. setlocal modifiable
  779. return 1
  780. endfunction
  781. function! s:switch_out(...)
  782. call winrestview(s:pos[-1])
  783. setlocal nomodifiable
  784. if a:0 > 0
  785. execute a:1
  786. endif
  787. if len(s:pos) > 1
  788. execute 'normal!' s:pos[0].'gt'
  789. execute s:pos[1] 'wincmd w'
  790. call winrestview(s:pos[2])
  791. endif
  792. endfunction
  793. function! s:finish_bindings()
  794. nnoremap <silent> <buffer> R :call <SID>retry()<cr>
  795. nnoremap <silent> <buffer> D :PlugDiff<cr>
  796. nnoremap <silent> <buffer> S :PlugStatus<cr>
  797. nnoremap <silent> <buffer> U :call <SID>status_update()<cr>
  798. xnoremap <silent> <buffer> U :call <SID>status_update()<cr>
  799. nnoremap <silent> <buffer> ]] :silent! call <SID>section('')<cr>
  800. nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr>
  801. endfunction
  802. function! s:prepare(...)
  803. if empty(s:plug_getcwd())
  804. throw 'Invalid current working directory. Cannot proceed.'
  805. endif
  806. for evar in ['$GIT_DIR', '$GIT_WORK_TREE']
  807. if exists(evar)
  808. throw evar.' detected. Cannot proceed.'
  809. endif
  810. endfor
  811. call s:job_abort(0)
  812. if s:switch_in()
  813. if b:plug_preview == 1
  814. pc
  815. endif
  816. enew
  817. else
  818. call s:new_window()
  819. endif
  820. nnoremap <silent> <buffer> q :call <SID>close_pane()<cr>
  821. if a:0 == 0
  822. call s:finish_bindings()
  823. endif
  824. let b:plug_preview = -1
  825. let s:plug_tab = tabpagenr()
  826. let s:plug_buf = winbufnr(0)
  827. call s:assign_name()
  828. for k in ['<cr>', 'L', 'o', 'X', 'd', 'dd']
  829. execute 'silent! unmap <buffer>' k
  830. endfor
  831. setlocal buftype=nofile bufhidden=wipe nobuflisted nolist noswapfile nowrap cursorline modifiable nospell
  832. if exists('+colorcolumn')
  833. setlocal colorcolumn=
  834. endif
  835. setf vim-plug
  836. if exists('g:syntax_on')
  837. call s:syntax()
  838. endif
  839. endfunction
  840. function! s:close_pane()
  841. if b:plug_preview == 1
  842. pc
  843. let b:plug_preview = -1
  844. elseif exists('s:jobs') && !empty(s:jobs)
  845. call s:job_abort(1)
  846. else
  847. bd
  848. endif
  849. endfunction
  850. function! s:assign_name()
  851. " Assign buffer name
  852. let prefix = '[Plugins]'
  853. let name = prefix
  854. let idx = 2
  855. while bufexists(name)
  856. let name = printf('%s (%s)', prefix, idx)
  857. let idx = idx + 1
  858. endwhile
  859. silent! execute 'f' fnameescape(name)
  860. endfunction
  861. function! s:chsh(swap)
  862. let prev = [&shell, &shellcmdflag, &shellredir]
  863. if !s:is_win
  864. set shell=sh
  865. endif
  866. if a:swap
  867. if s:is_powershell(&shell)
  868. let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s'
  869. elseif &shell =~# 'sh' || &shell =~# 'cmd\(\.exe\)\?$'
  870. set shellredir=>%s\ 2>&1
  871. endif
  872. endif
  873. return prev
  874. endfunction
  875. function! s:bang(cmd, ...)
  876. let batchfile = ''
  877. try
  878. let [sh, shellcmdflag, shrd] = s:chsh(a:0)
  879. " FIXME: Escaping is incomplete. We could use shellescape with eval,
  880. " but it won't work on Windows.
  881. let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd
  882. if s:is_win
  883. let [batchfile, cmd] = s:batchfile(cmd)
  884. endif
  885. let g:_plug_bang = (s:is_win && has('gui_running') ? 'silent ' : '').'!'.escape(cmd, '#!%')
  886. execute "normal! :execute g:_plug_bang\<cr>\<cr>"
  887. finally
  888. unlet g:_plug_bang
  889. let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
  890. if s:is_win && filereadable(batchfile)
  891. call delete(batchfile)
  892. endif
  893. endtry
  894. return v:shell_error ? 'Exit status: ' . v:shell_error : ''
  895. endfunction
  896. function! s:regress_bar()
  897. let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '')
  898. call s:progress_bar(2, bar, len(bar))
  899. endfunction
  900. function! s:is_updated(dir)
  901. return !empty(s:system_chomp(['git', 'log', '--pretty=format:%h', 'HEAD...HEAD@{1}'], a:dir))
  902. endfunction
  903. function! s:do(pull, force, todo)
  904. if has('nvim')
  905. " Reset &rtp to invalidate Neovim cache of loaded Lua modules
  906. " See https://github.com/junegunn/vim-plug/pull/1157#issuecomment-1809226110
  907. let &rtp = &rtp
  908. endif
  909. for [name, spec] in items(a:todo)
  910. if !isdirectory(spec.dir)
  911. continue
  912. endif
  913. let installed = has_key(s:update.new, name)
  914. let updated = installed ? 0 :
  915. \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir))
  916. if a:force || installed || updated
  917. execute 'cd' s:esc(spec.dir)
  918. call append(3, '- Post-update hook for '. name .' ... ')
  919. let error = ''
  920. let type = type(spec.do)
  921. if type == s:TYPE.string
  922. if spec.do[0] == ':'
  923. if !get(s:loaded, name, 0)
  924. let s:loaded[name] = 1
  925. call s:reorg_rtp()
  926. endif
  927. call s:load_plugin(spec)
  928. try
  929. execute spec.do[1:]
  930. catch
  931. let error = v:exception
  932. endtry
  933. if !s:plug_window_exists()
  934. cd -
  935. throw 'Warning: vim-plug was terminated by the post-update hook of '.name
  936. endif
  937. else
  938. let error = s:bang(spec.do)
  939. endif
  940. elseif type == s:TYPE.funcref
  941. try
  942. call s:load_plugin(spec)
  943. let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged')
  944. call spec.do({ 'name': name, 'status': status, 'force': a:force })
  945. catch
  946. let error = v:exception
  947. endtry
  948. else
  949. let error = 'Invalid hook type'
  950. endif
  951. call s:switch_in()
  952. call setline(4, empty(error) ? (getline(4) . 'OK')
  953. \ : ('x' . getline(4)[1:] . error))
  954. if !empty(error)
  955. call add(s:update.errors, name)
  956. call s:regress_bar()
  957. endif
  958. cd -
  959. endif
  960. endfor
  961. endfunction
  962. function! s:hash_match(a, b)
  963. return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0
  964. endfunction
  965. function! s:checkout(spec)
  966. let sha = a:spec.commit
  967. let output = s:git_revision(a:spec.dir)
  968. let error = 0
  969. if !empty(output) && !s:hash_match(sha, s:lines(output)[0])
  970. let credential_helper = s:git_version_requirement(2) ? '-c credential.helper= ' : ''
  971. let output = s:system(
  972. \ 'git '.credential_helper.'fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir)
  973. let error = v:shell_error
  974. endif
  975. return [output, error]
  976. endfunction
  977. function! s:finish(pull)
  978. let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen'))
  979. if new_frozen
  980. let s = new_frozen > 1 ? 's' : ''
  981. call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s))
  982. endif
  983. call append(3, '- Finishing ... ') | 4
  984. redraw
  985. call plug#helptags()
  986. call plug#end()
  987. call setline(4, getline(4) . 'Done!')
  988. redraw
  989. let msgs = []
  990. if !empty(s:update.errors)
  991. call add(msgs, "Press 'R' to retry.")
  992. endif
  993. if a:pull && len(s:update.new) < len(filter(getline(5, '$'),
  994. \ "v:val =~ '^- ' && v:val !~# 'Already up.to.date'"))
  995. call add(msgs, "Press 'D' to see the updated changes.")
  996. endif
  997. echo join(msgs, ' ')
  998. call s:finish_bindings()
  999. endfunction
  1000. function! s:retry()
  1001. if empty(s:update.errors)
  1002. return
  1003. endif
  1004. echo
  1005. call s:update_impl(s:update.pull, s:update.force,
  1006. \ extend(copy(s:update.errors), [s:update.threads]))
  1007. endfunction
  1008. function! s:is_managed(name)
  1009. return has_key(g:plugs[a:name], 'uri')
  1010. endfunction
  1011. function! s:names(...)
  1012. return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)'))
  1013. endfunction
  1014. function! s:check_ruby()
  1015. silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'")
  1016. if !exists('g:plug_ruby')
  1017. redraw!
  1018. return s:warn('echom', 'Warning: Ruby interface is broken')
  1019. endif
  1020. let ruby_version = split(g:plug_ruby, '\.')
  1021. unlet g:plug_ruby
  1022. return s:version_requirement(ruby_version, [1, 8, 7])
  1023. endfunction
  1024. function! s:update_impl(pull, force, args) abort
  1025. let sync = index(a:args, '--sync') >= 0 || has('vim_starting')
  1026. let args = filter(copy(a:args), 'v:val != "--sync"')
  1027. let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
  1028. \ remove(args, -1) : get(g:, 'plug_threads', 16)
  1029. let managed = filter(deepcopy(g:plugs), 's:is_managed(v:key)')
  1030. let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') :
  1031. \ filter(managed, 'index(args, v:key) >= 0')
  1032. if empty(todo)
  1033. return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install'))
  1034. endif
  1035. if !s:is_win && s:git_version_requirement(2, 3)
  1036. let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : ''
  1037. let $GIT_TERMINAL_PROMPT = 0
  1038. for plug in values(todo)
  1039. let plug.uri = substitute(plug.uri,
  1040. \ '^https://git::@github\.com', 'https://github.com', '')
  1041. endfor
  1042. endif
  1043. if !isdirectory(g:plug_home)
  1044. try
  1045. call mkdir(g:plug_home, 'p')
  1046. catch
  1047. return s:err(printf('Invalid plug directory: %s. '.
  1048. \ 'Try to call plug#begin with a valid directory', g:plug_home))
  1049. endtry
  1050. endif
  1051. if has('nvim') && !exists('*jobwait') && threads > 1
  1052. call s:warn('echom', '[vim-plug] Update Neovim for parallel installer')
  1053. endif
  1054. let use_job = s:nvim || s:vim8
  1055. let python = (has('python') || has('python3')) && !use_job
  1056. let ruby = has('ruby') && !use_job && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && threads > 1 && s:check_ruby()
  1057. let s:update = {
  1058. \ 'start': reltime(),
  1059. \ 'all': todo,
  1060. \ 'todo': copy(todo),
  1061. \ 'errors': [],
  1062. \ 'pull': a:pull,
  1063. \ 'force': a:force,
  1064. \ 'new': {},
  1065. \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1,
  1066. \ 'bar': '',
  1067. \ 'fin': 0
  1068. \ }
  1069. call s:prepare(1)
  1070. call append(0, ['', ''])
  1071. normal! 2G
  1072. silent! redraw
  1073. " Set remote name, overriding a possible user git config's clone.defaultRemoteName
  1074. let s:clone_opt = ['--origin', 'origin']
  1075. if get(g:, 'plug_shallow', 1)
  1076. call extend(s:clone_opt, ['--depth', '1'])
  1077. if s:git_version_requirement(1, 7, 10)
  1078. call add(s:clone_opt, '--no-single-branch')
  1079. endif
  1080. endif
  1081. if has('win32unix') || has('wsl')
  1082. call extend(s:clone_opt, ['-c', 'core.eol=lf', '-c', 'core.autocrlf=input'])
  1083. endif
  1084. let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : ''
  1085. " Python version requirement (>= 2.7)
  1086. if python && !has('python3') && !ruby && !use_job && s:update.threads > 1
  1087. redir => pyv
  1088. silent python import platform; print platform.python_version()
  1089. redir END
  1090. let python = s:version_requirement(
  1091. \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6])
  1092. endif
  1093. if (python || ruby) && s:update.threads > 1
  1094. try
  1095. let imd = &imd
  1096. if s:mac_gui
  1097. set noimd
  1098. endif
  1099. if ruby
  1100. call s:update_ruby()
  1101. else
  1102. call s:update_python()
  1103. endif
  1104. catch
  1105. let lines = getline(4, '$')
  1106. let printed = {}
  1107. silent! 4,$d _
  1108. for line in lines
  1109. let name = s:extract_name(line, '.', '')
  1110. if empty(name) || !has_key(printed, name)
  1111. call append('$', line)
  1112. if !empty(name)
  1113. let printed[name] = 1
  1114. if line[0] == 'x' && index(s:update.errors, name) < 0
  1115. call add(s:update.errors, name)
  1116. end
  1117. endif
  1118. endif
  1119. endfor
  1120. finally
  1121. let &imd = imd
  1122. call s:update_finish()
  1123. endtry
  1124. else
  1125. call s:update_vim()
  1126. while use_job && sync
  1127. sleep 100m
  1128. if s:update.fin
  1129. break
  1130. endif
  1131. endwhile
  1132. endif
  1133. endfunction
  1134. function! s:log4(name, msg)
  1135. call setline(4, printf('- %s (%s)', a:msg, a:name))
  1136. redraw
  1137. endfunction
  1138. function! s:update_finish()
  1139. if exists('s:git_terminal_prompt')
  1140. let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt
  1141. endif
  1142. if s:switch_in()
  1143. call append(3, '- Updating ...') | 4
  1144. for [name, spec] in items(filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && (s:update.force || s:update.pull || has_key(s:update.new, v:key))'))
  1145. let [pos, _] = s:logpos(name)
  1146. if !pos
  1147. continue
  1148. endif
  1149. let out = ''
  1150. let error = 0
  1151. if has_key(spec, 'commit')
  1152. call s:log4(name, 'Checking out '.spec.commit)
  1153. let [out, error] = s:checkout(spec)
  1154. elseif has_key(spec, 'tag')
  1155. let tag = spec.tag
  1156. if tag =~ '\*'
  1157. let tags = s:lines(s:system('git tag --list '.plug#shellescape(tag).' --sort -version:refname 2>&1', spec.dir))
  1158. if !v:shell_error && !empty(tags)
  1159. let tag = tags[0]
  1160. call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag))
  1161. call append(3, '')
  1162. endif
  1163. endif
  1164. call s:log4(name, 'Checking out '.tag)
  1165. let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir)
  1166. let error = v:shell_error
  1167. endif
  1168. if !error && filereadable(spec.dir.'/.gitmodules') &&
  1169. \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir))
  1170. call s:log4(name, 'Updating submodules. This may take a while.')
  1171. let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir)
  1172. let error = v:shell_error
  1173. endif
  1174. let msg = s:format_message(v:shell_error ? 'x': '-', name, out)
  1175. if error
  1176. call add(s:update.errors, name)
  1177. call s:regress_bar()
  1178. silent execute pos 'd _'
  1179. call append(4, msg) | 4
  1180. elseif !empty(out)
  1181. call setline(pos, msg[0])
  1182. endif
  1183. redraw
  1184. endfor
  1185. silent 4 d _
  1186. try
  1187. call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && has_key(v:val, "do")'))
  1188. catch
  1189. call s:warn('echom', v:exception)
  1190. call s:warn('echo', '')
  1191. return
  1192. endtry
  1193. call s:finish(s:update.pull)
  1194. call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.')
  1195. call s:switch_out('normal! gg')
  1196. endif
  1197. endfunction
  1198. function! s:mark_aborted(name, message)
  1199. let attrs = { 'running': 0, 'error': 1, 'abort': 1, 'lines': [a:message] }
  1200. let s:jobs[a:name] = extend(get(s:jobs, a:name, {}), attrs)
  1201. endfunction
  1202. function! s:job_abort(cancel)
  1203. if (!s:nvim && !s:vim8) || !exists('s:jobs')
  1204. return
  1205. endif
  1206. for [name, j] in items(s:jobs)
  1207. if s:nvim
  1208. silent! call jobstop(j.jobid)
  1209. elseif s:vim8
  1210. silent! call job_stop(j.jobid)
  1211. endif
  1212. if j.new
  1213. call s:rm_rf(g:plugs[name].dir)
  1214. endif
  1215. if a:cancel
  1216. call s:mark_aborted(name, 'Aborted')
  1217. endif
  1218. endfor
  1219. if a:cancel
  1220. for todo in values(s:update.todo)
  1221. let todo.abort = 1
  1222. endfor
  1223. else
  1224. let s:jobs = {}
  1225. endif
  1226. endfunction
  1227. function! s:last_non_empty_line(lines)
  1228. let len = len(a:lines)
  1229. for idx in range(len)
  1230. let line = a:lines[len-idx-1]
  1231. if !empty(line)
  1232. return line
  1233. endif
  1234. endfor
  1235. return ''
  1236. endfunction
  1237. function! s:bullet_for(job, ...)
  1238. if a:job.running
  1239. return a:job.new ? '+' : '*'
  1240. endif
  1241. if get(a:job, 'abort', 0)
  1242. return '~'
  1243. endif
  1244. return a:job.error ? 'x' : get(a:000, 0, '-')
  1245. endfunction
  1246. function! s:job_out_cb(self, data) abort
  1247. let self = a:self
  1248. let data = remove(self.lines, -1) . a:data
  1249. let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]')
  1250. call extend(self.lines, lines)
  1251. " To reduce the number of buffer updates
  1252. let self.tick = get(self, 'tick', -1) + 1
  1253. if !self.running || self.tick % len(s:jobs) == 0
  1254. let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines)
  1255. if len(result)
  1256. call s:log(s:bullet_for(self), self.name, result)
  1257. endif
  1258. endif
  1259. endfunction
  1260. function! s:job_exit_cb(self, data) abort
  1261. let a:self.running = 0
  1262. let a:self.error = a:data != 0
  1263. call s:reap(a:self.name)
  1264. call s:tick()
  1265. endfunction
  1266. function! s:job_cb(fn, job, ch, data)
  1267. if !s:plug_window_exists() " plug window closed
  1268. return s:job_abort(0)
  1269. endif
  1270. call call(a:fn, [a:job, a:data])
  1271. endfunction
  1272. function! s:nvim_cb(job_id, data, event) dict abort
  1273. return (a:event == 'stdout' || a:event == 'stderr') ?
  1274. \ s:job_cb('s:job_out_cb', self, 0, join(a:data, "\n")) :
  1275. \ s:job_cb('s:job_exit_cb', self, 0, a:data)
  1276. endfunction
  1277. function! s:spawn(name, spec, queue, opts)
  1278. let job = { 'name': a:name, 'spec': a:spec, 'running': 1, 'error': 0, 'lines': [''],
  1279. \ 'new': get(a:opts, 'new', 0), 'queue': copy(a:queue) }
  1280. let Item = remove(job.queue, 0)
  1281. let argv = type(Item) == s:TYPE.funcref ? call(Item, [a:spec]) : Item
  1282. let s:jobs[a:name] = job
  1283. if s:nvim
  1284. if has_key(a:opts, 'dir')
  1285. let job.cwd = a:opts.dir
  1286. endif
  1287. call extend(job, {
  1288. \ 'on_stdout': function('s:nvim_cb'),
  1289. \ 'on_stderr': function('s:nvim_cb'),
  1290. \ 'on_exit': function('s:nvim_cb'),
  1291. \ })
  1292. let jid = s:plug_call('jobstart', argv, job)
  1293. if jid > 0
  1294. let job.jobid = jid
  1295. else
  1296. let job.running = 0
  1297. let job.error = 1
  1298. let job.lines = [jid < 0 ? argv[0].' is not executable' :
  1299. \ 'Invalid arguments (or job table is full)']
  1300. endif
  1301. elseif s:vim8
  1302. let cmd = join(map(copy(argv), 'plug#shellescape(v:val, {"script": 0})'))
  1303. if has_key(a:opts, 'dir')
  1304. let cmd = s:with_cd(cmd, a:opts.dir, 0)
  1305. endif
  1306. let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd]
  1307. let jid = job_start(s:is_win ? join(argv, ' ') : argv, {
  1308. \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]),
  1309. \ 'err_cb': function('s:job_cb', ['s:job_out_cb', job]),
  1310. \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]),
  1311. \ 'err_mode': 'raw',
  1312. \ 'out_mode': 'raw'
  1313. \})
  1314. if job_status(jid) == 'run'
  1315. let job.jobid = jid
  1316. else
  1317. let job.running = 0
  1318. let job.error = 1
  1319. let job.lines = ['Failed to start job']
  1320. endif
  1321. else
  1322. let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [argv, a:opts.dir] : [argv]))
  1323. let job.error = v:shell_error != 0
  1324. let job.running = 0
  1325. endif
  1326. endfunction
  1327. function! s:reap(name)
  1328. let job = remove(s:jobs, a:name)
  1329. if job.error
  1330. call add(s:update.errors, a:name)
  1331. elseif get(job, 'new', 0)
  1332. let s:update.new[a:name] = 1
  1333. endif
  1334. let more = len(get(job, 'queue', []))
  1335. let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines)
  1336. if len(result)
  1337. call s:log(s:bullet_for(job), a:name, result)
  1338. endif
  1339. if !job.error && more
  1340. let job.spec.queue = job.queue
  1341. let s:update.todo[a:name] = job.spec
  1342. else
  1343. let s:update.bar .= s:bullet_for(job, '=')
  1344. call s:bar()
  1345. endif
  1346. endfunction
  1347. function! s:bar()
  1348. if s:switch_in()
  1349. let total = len(s:update.all)
  1350. call setline(1, (s:update.pull ? 'Updating' : 'Installing').
  1351. \ ' plugins ('.len(s:update.bar).'/'.total.')')
  1352. call s:progress_bar(2, s:update.bar, total)
  1353. call s:switch_out()
  1354. endif
  1355. endfunction
  1356. function! s:logpos(name)
  1357. let max = line('$')
  1358. for i in range(4, max > 4 ? max : 4)
  1359. if getline(i) =~# '^[-+x*] '.a:name.':'
  1360. for j in range(i + 1, max > 5 ? max : 5)
  1361. if getline(j) !~ '^ '
  1362. return [i, j - 1]
  1363. endif
  1364. endfor
  1365. return [i, i]
  1366. endif
  1367. endfor
  1368. return [0, 0]
  1369. endfunction
  1370. function! s:log(bullet, name, lines)
  1371. if s:switch_in()
  1372. let [b, e] = s:logpos(a:name)
  1373. if b > 0
  1374. silent execute printf('%d,%d d _', b, e)
  1375. if b > winheight('.')
  1376. let b = 4
  1377. endif
  1378. else
  1379. let b = 4
  1380. endif
  1381. " FIXME For some reason, nomodifiable is set after :d in vim8
  1382. setlocal modifiable
  1383. call append(b - 1, s:format_message(a:bullet, a:name, a:lines))
  1384. call s:switch_out()
  1385. endif
  1386. endfunction
  1387. function! s:update_vim()
  1388. let s:jobs = {}
  1389. call s:bar()
  1390. call s:tick()
  1391. endfunction
  1392. function! s:checkout_command(spec)
  1393. let a:spec.branch = s:git_origin_branch(a:spec)
  1394. return ['git', 'checkout', '-q', a:spec.branch, '--']
  1395. endfunction
  1396. function! s:merge_command(spec)
  1397. let a:spec.branch = s:git_origin_branch(a:spec)
  1398. return ['git', 'merge', '--ff-only', 'origin/'.a:spec.branch]
  1399. endfunction
  1400. function! s:tick()
  1401. let pull = s:update.pull
  1402. let prog = s:progress_opt(s:nvim || s:vim8)
  1403. while 1 " Without TCO, Vim stack is bound to explode
  1404. if empty(s:update.todo)
  1405. if empty(s:jobs) && !s:update.fin
  1406. call s:update_finish()
  1407. let s:update.fin = 1
  1408. endif
  1409. return
  1410. endif
  1411. let name = keys(s:update.todo)[0]
  1412. let spec = remove(s:update.todo, name)
  1413. if get(spec, 'abort', 0)
  1414. call s:mark_aborted(name, 'Skipped')
  1415. call s:reap(name)
  1416. continue
  1417. endif
  1418. let queue = get(spec, 'queue', [])
  1419. let new = empty(globpath(spec.dir, '.git', 1))
  1420. if empty(queue)
  1421. call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...')
  1422. redraw
  1423. endif
  1424. let has_tag = has_key(spec, 'tag')
  1425. if len(queue)
  1426. call s:spawn(name, spec, queue, { 'dir': spec.dir })
  1427. elseif !new
  1428. let [error, _] = s:git_validate(spec, 0)
  1429. if empty(error)
  1430. if pull
  1431. let cmd = s:git_version_requirement(2) ? ['git', '-c', 'credential.helper=', 'fetch'] : ['git', 'fetch']
  1432. if has_tag && !empty(globpath(spec.dir, '.git/shallow'))
  1433. call extend(cmd, ['--depth', '99999999'])
  1434. endif
  1435. if !empty(prog)
  1436. call add(cmd, prog)
  1437. endif
  1438. let queue = [cmd, split('git remote set-head origin -a')]
  1439. if !has_tag && !has_key(spec, 'commit')
  1440. call extend(queue, [function('s:checkout_command'), function('s:merge_command')])
  1441. endif
  1442. call s:spawn(name, spec, queue, { 'dir': spec.dir })
  1443. else
  1444. let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 }
  1445. endif
  1446. else
  1447. let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 }
  1448. endif
  1449. else
  1450. let cmd = ['git', 'clone']
  1451. if !has_tag
  1452. call extend(cmd, s:clone_opt)
  1453. endif
  1454. if !empty(prog)
  1455. call add(cmd, prog)
  1456. endif
  1457. call s:spawn(name, spec, [extend(cmd, [spec.uri, s:trim(spec.dir)]), function('s:checkout_command'), function('s:merge_command')], { 'new': 1 })
  1458. endif
  1459. if !s:jobs[name].running
  1460. call s:reap(name)
  1461. endif
  1462. if len(s:jobs) >= s:update.threads
  1463. break
  1464. endif
  1465. endwhile
  1466. endfunction
  1467. function! s:update_python()
  1468. let py_exe = has('python') ? 'python' : 'python3'
  1469. execute py_exe "<< EOF"
  1470. import datetime
  1471. import functools
  1472. import os
  1473. try:
  1474. import queue
  1475. except ImportError:
  1476. import Queue as queue
  1477. import random
  1478. import re
  1479. import shutil
  1480. import signal
  1481. import subprocess
  1482. import tempfile
  1483. import threading as thr
  1484. import time
  1485. import traceback
  1486. import vim
  1487. G_NVIM = vim.eval("has('nvim')") == '1'
  1488. G_PULL = vim.eval('s:update.pull') == '1'
  1489. G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1
  1490. G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)'))
  1491. G_CLONE_OPT = ' '.join(vim.eval('s:clone_opt'))
  1492. G_PROGRESS = vim.eval('s:progress_opt(1)')
  1493. G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads'))
  1494. G_STOP = thr.Event()
  1495. G_IS_WIN = vim.eval('s:is_win') == '1'
  1496. class PlugError(Exception):
  1497. def __init__(self, msg):
  1498. self.msg = msg
  1499. class CmdTimedOut(PlugError):
  1500. pass
  1501. class CmdFailed(PlugError):
  1502. pass
  1503. class InvalidURI(PlugError):
  1504. pass
  1505. class Action(object):
  1506. INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-']
  1507. class Buffer(object):
  1508. def __init__(self, lock, num_plugs, is_pull):
  1509. self.bar = ''
  1510. self.event = 'Updating' if is_pull else 'Installing'
  1511. self.lock = lock
  1512. self.maxy = int(vim.eval('winheight(".")'))
  1513. self.num_plugs = num_plugs
  1514. def __where(self, name):
  1515. """ Find first line with name in current buffer. Return line num. """
  1516. found, lnum = False, 0
  1517. matcher = re.compile('^[-+x*] {0}:'.format(name))
  1518. for line in vim.current.buffer:
  1519. if matcher.search(line) is not None:
  1520. found = True
  1521. break
  1522. lnum += 1
  1523. if not found:
  1524. lnum = -1
  1525. return lnum
  1526. def header(self):
  1527. curbuf = vim.current.buffer
  1528. curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs)
  1529. num_spaces = self.num_plugs - len(self.bar)
  1530. curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ')
  1531. with self.lock:
  1532. vim.command('normal! 2G')
  1533. vim.command('redraw')
  1534. def write(self, action, name, lines):
  1535. first, rest = lines[0], lines[1:]
  1536. msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)]
  1537. msg.extend([' ' + line for line in rest])
  1538. try:
  1539. if action == Action.ERROR:
  1540. self.bar += 'x'
  1541. vim.command("call add(s:update.errors, '{0}')".format(name))
  1542. elif action == Action.DONE:
  1543. self.bar += '='
  1544. curbuf = vim.current.buffer
  1545. lnum = self.__where(name)
  1546. if lnum != -1: # Found matching line num
  1547. del curbuf[lnum]
  1548. if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]):
  1549. lnum = 3
  1550. else:
  1551. lnum = 3
  1552. curbuf.append(msg, lnum)
  1553. self.header()
  1554. except vim.error:
  1555. pass
  1556. class Command(object):
  1557. CD = 'cd /d' if G_IS_WIN else 'cd'
  1558. def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None):
  1559. self.cmd = cmd
  1560. if cmd_dir:
  1561. self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd)
  1562. self.timeout = timeout
  1563. self.callback = cb if cb else (lambda msg: None)
  1564. self.clean = clean if clean else (lambda: None)
  1565. self.proc = None
  1566. @property
  1567. def alive(self):
  1568. """ Returns true only if command still running. """
  1569. return self.proc and self.proc.poll() is None
  1570. def execute(self, ntries=3):
  1571. """ Execute the command with ntries if CmdTimedOut.
  1572. Returns the output of the command if no Exception.
  1573. """
  1574. attempt, finished, limit = 0, False, self.timeout
  1575. while not finished:
  1576. try:
  1577. attempt += 1
  1578. result = self.try_command()
  1579. finished = True
  1580. return result
  1581. except CmdTimedOut:
  1582. if attempt != ntries:
  1583. self.notify_retry()
  1584. self.timeout += limit
  1585. else:
  1586. raise
  1587. def notify_retry(self):
  1588. """ Retry required for command, notify user. """
  1589. for count in range(3, 0, -1):
  1590. if G_STOP.is_set():
  1591. raise KeyboardInterrupt
  1592. msg = 'Timeout. Will retry in {0} second{1} ...'.format(
  1593. count, 's' if count != 1 else '')
  1594. self.callback([msg])
  1595. time.sleep(1)
  1596. self.callback(['Retrying ...'])
  1597. def try_command(self):
  1598. """ Execute a cmd & poll for callback. Returns list of output.
  1599. Raises CmdFailed -> return code for Popen isn't 0
  1600. Raises CmdTimedOut -> command exceeded timeout without new output
  1601. """
  1602. first_line = True
  1603. try:
  1604. tfile = tempfile.NamedTemporaryFile(mode='w+b')
  1605. preexec_fn = not G_IS_WIN and os.setsid or None
  1606. self.proc = subprocess.Popen(self.cmd, stdout=tfile,
  1607. stderr=subprocess.STDOUT,
  1608. stdin=subprocess.PIPE, shell=True,
  1609. preexec_fn=preexec_fn)
  1610. thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,))
  1611. thrd.start()
  1612. thread_not_started = True
  1613. while thread_not_started:
  1614. try:
  1615. thrd.join(0.1)
  1616. thread_not_started = False
  1617. except RuntimeError:
  1618. pass
  1619. while self.alive:
  1620. if G_STOP.is_set():
  1621. raise KeyboardInterrupt
  1622. if first_line or random.random() < G_LOG_PROB:
  1623. first_line = False
  1624. line = '' if G_IS_WIN else nonblock_read(tfile.name)
  1625. if line:
  1626. self.callback([line])
  1627. time_diff = time.time() - os.path.getmtime(tfile.name)
  1628. if time_diff > self.timeout:
  1629. raise CmdTimedOut(['Timeout!'])
  1630. thrd.join(0.5)
  1631. tfile.seek(0)
  1632. result = [line.decode('utf-8', 'replace').rstrip() for line in tfile]
  1633. if self.proc.returncode != 0:
  1634. raise CmdFailed([''] + result)
  1635. return result
  1636. except:
  1637. self.terminate()
  1638. raise
  1639. def terminate(self):
  1640. """ Terminate process and cleanup. """
  1641. if self.alive:
  1642. if G_IS_WIN:
  1643. os.kill(self.proc.pid, signal.SIGINT)
  1644. else:
  1645. os.killpg(self.proc.pid, signal.SIGTERM)
  1646. self.clean()
  1647. class Plugin(object):
  1648. def __init__(self, name, args, buf_q, lock):
  1649. self.name = name
  1650. self.args = args
  1651. self.buf_q = buf_q
  1652. self.lock = lock
  1653. self.tag = args.get('tag', 0)
  1654. def manage(self):
  1655. try:
  1656. if os.path.exists(self.args['dir']):
  1657. self.update()
  1658. else:
  1659. self.install()
  1660. with self.lock:
  1661. thread_vim_command("let s:update.new['{0}'] = 1".format(self.name))
  1662. except PlugError as exc:
  1663. self.write(Action.ERROR, self.name, exc.msg)
  1664. except KeyboardInterrupt:
  1665. G_STOP.set()
  1666. self.write(Action.ERROR, self.name, ['Interrupted!'])
  1667. except:
  1668. # Any exception except those above print stack trace
  1669. msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip())
  1670. self.write(Action.ERROR, self.name, msg.split('\n'))
  1671. raise
  1672. def install(self):
  1673. target = self.args['dir']
  1674. if target[-1] == '\\':
  1675. target = target[0:-1]
  1676. def clean(target):
  1677. def _clean():
  1678. try:
  1679. shutil.rmtree(target)
  1680. except OSError:
  1681. pass
  1682. return _clean
  1683. self.write(Action.INSTALL, self.name, ['Installing ...'])
  1684. callback = functools.partial(self.write, Action.INSTALL, self.name)
  1685. cmd = 'git clone {0} {1} {2} {3} 2>&1'.format(
  1686. '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'],
  1687. esc(target))
  1688. com = Command(cmd, None, G_TIMEOUT, callback, clean(target))
  1689. result = com.execute(G_RETRIES)
  1690. self.write(Action.DONE, self.name, result[-1:])
  1691. def repo_uri(self):
  1692. cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url'
  1693. command = Command(cmd, self.args['dir'], G_TIMEOUT,)
  1694. result = command.execute(G_RETRIES)
  1695. return result[-1]
  1696. def update(self):
  1697. actual_uri = self.repo_uri()
  1698. expect_uri = self.args['uri']
  1699. regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$')
  1700. ma = regex.match(actual_uri)
  1701. mb = regex.match(expect_uri)
  1702. if ma is None or mb is None or ma.groups() != mb.groups():
  1703. msg = ['',
  1704. 'Invalid URI: {0}'.format(actual_uri),
  1705. 'Expected {0}'.format(expect_uri),
  1706. 'PlugClean required.']
  1707. raise InvalidURI(msg)
  1708. if G_PULL:
  1709. self.write(Action.UPDATE, self.name, ['Updating ...'])
  1710. callback = functools.partial(self.write, Action.UPDATE, self.name)
  1711. fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else ''
  1712. cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS)
  1713. com = Command(cmd, self.args['dir'], G_TIMEOUT, callback)
  1714. result = com.execute(G_RETRIES)
  1715. self.write(Action.DONE, self.name, result[-1:])
  1716. else:
  1717. self.write(Action.DONE, self.name, ['Already installed'])
  1718. def write(self, action, name, msg):
  1719. self.buf_q.put((action, name, msg))
  1720. class PlugThread(thr.Thread):
  1721. def __init__(self, tname, args):
  1722. super(PlugThread, self).__init__()
  1723. self.tname = tname
  1724. self.args = args
  1725. def run(self):
  1726. thr.current_thread().name = self.tname
  1727. buf_q, work_q, lock = self.args
  1728. try:
  1729. while not G_STOP.is_set():
  1730. name, args = work_q.get_nowait()
  1731. plug = Plugin(name, args, buf_q, lock)
  1732. plug.manage()
  1733. work_q.task_done()
  1734. except queue.Empty:
  1735. pass
  1736. class RefreshThread(thr.Thread):
  1737. def __init__(self, lock):
  1738. super(RefreshThread, self).__init__()
  1739. self.lock = lock
  1740. self.running = True
  1741. def run(self):
  1742. while self.running:
  1743. with self.lock:
  1744. thread_vim_command('noautocmd normal! a')
  1745. time.sleep(0.33)
  1746. def stop(self):
  1747. self.running = False
  1748. if G_NVIM:
  1749. def thread_vim_command(cmd):
  1750. vim.session.threadsafe_call(lambda: vim.command(cmd))
  1751. else:
  1752. def thread_vim_command(cmd):
  1753. vim.command(cmd)
  1754. def esc(name):
  1755. return '"' + name.replace('"', '\"') + '"'
  1756. def nonblock_read(fname):
  1757. """ Read a file with nonblock flag. Return the last line. """
  1758. fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK)
  1759. buf = os.read(fread, 100000).decode('utf-8', 'replace')
  1760. os.close(fread)
  1761. line = buf.rstrip('\r\n')
  1762. left = max(line.rfind('\r'), line.rfind('\n'))
  1763. if left != -1:
  1764. left += 1
  1765. line = line[left:]
  1766. return line
  1767. def main():
  1768. thr.current_thread().name = 'main'
  1769. nthreads = int(vim.eval('s:update.threads'))
  1770. plugs = vim.eval('s:update.todo')
  1771. mac_gui = vim.eval('s:mac_gui') == '1'
  1772. lock = thr.Lock()
  1773. buf = Buffer(lock, len(plugs), G_PULL)
  1774. buf_q, work_q = queue.Queue(), queue.Queue()
  1775. for work in plugs.items():
  1776. work_q.put(work)
  1777. start_cnt = thr.active_count()
  1778. for num in range(nthreads):
  1779. tname = 'PlugT-{0:02}'.format(num)
  1780. thread = PlugThread(tname, (buf_q, work_q, lock))
  1781. thread.start()
  1782. if mac_gui:
  1783. rthread = RefreshThread(lock)
  1784. rthread.start()
  1785. while not buf_q.empty() or thr.active_count() != start_cnt:
  1786. try:
  1787. action, name, msg = buf_q.get(True, 0.25)
  1788. buf.write(action, name, ['OK'] if not msg else msg)
  1789. buf_q.task_done()
  1790. except queue.Empty:
  1791. pass
  1792. except KeyboardInterrupt:
  1793. G_STOP.set()
  1794. if mac_gui:
  1795. rthread.stop()
  1796. rthread.join()
  1797. main()
  1798. EOF
  1799. endfunction
  1800. function! s:update_ruby()
  1801. ruby << EOF
  1802. module PlugStream
  1803. SEP = ["\r", "\n", nil]
  1804. def get_line
  1805. buffer = ''
  1806. loop do
  1807. char = readchar rescue return
  1808. if SEP.include? char.chr
  1809. buffer << $/
  1810. break
  1811. else
  1812. buffer << char
  1813. end
  1814. end
  1815. buffer
  1816. end
  1817. end unless defined?(PlugStream)
  1818. def esc arg
  1819. %["#{arg.gsub('"', '\"')}"]
  1820. end
  1821. def killall pid
  1822. pids = [pid]
  1823. if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM
  1824. pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil }
  1825. else
  1826. unless `which pgrep 2> /dev/null`.empty?
  1827. children = pids
  1828. until children.empty?
  1829. children = children.map { |pid|
  1830. `pgrep -P #{pid}`.lines.map { |l| l.chomp }
  1831. }.flatten
  1832. pids += children
  1833. end
  1834. end
  1835. pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
  1836. end
  1837. end
  1838. def compare_git_uri a, b
  1839. regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$}
  1840. regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1)
  1841. end
  1842. require 'thread'
  1843. require 'fileutils'
  1844. require 'timeout'
  1845. running = true
  1846. iswin = VIM::evaluate('s:is_win').to_i == 1
  1847. pull = VIM::evaluate('s:update.pull').to_i == 1
  1848. base = VIM::evaluate('g:plug_home')
  1849. all = VIM::evaluate('s:update.todo')
  1850. limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
  1851. tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1
  1852. nthr = VIM::evaluate('s:update.threads').to_i
  1853. maxy = VIM::evaluate('winheight(".")').to_i
  1854. vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/
  1855. cd = iswin ? 'cd /d' : 'cd'
  1856. tot = VIM::evaluate('len(s:update.todo)') || 0
  1857. bar = ''
  1858. skip = 'Already installed'
  1859. mtx = Mutex.new
  1860. take1 = proc { mtx.synchronize { running && all.shift } }
  1861. logh = proc {
  1862. cnt = bar.length
  1863. $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
  1864. $curbuf[2] = '[' + bar.ljust(tot) + ']'
  1865. VIM::command('normal! 2G')
  1866. VIM::command('redraw')
  1867. }
  1868. where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } }
  1869. log = proc { |name, result, type|
  1870. mtx.synchronize do
  1871. ing = ![true, false].include?(type)
  1872. bar += type ? '=' : 'x' unless ing
  1873. b = case type
  1874. when :install then '+' when :update then '*'
  1875. when true, nil then '-' else
  1876. VIM::command("call add(s:update.errors, '#{name}')")
  1877. 'x'
  1878. end
  1879. result =
  1880. if type || type.nil?
  1881. ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"]
  1882. elsif result =~ /^Interrupted|^Timeout/
  1883. ["#{b} #{name}: #{result}"]
  1884. else
  1885. ["#{b} #{name}"] + result.lines.map { |l| " " << l }
  1886. end
  1887. if lnum = where.call(name)
  1888. $curbuf.delete lnum
  1889. lnum = 4 if ing && lnum > maxy
  1890. end
  1891. result.each_with_index do |line, offset|
  1892. $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp)
  1893. end
  1894. logh.call
  1895. end
  1896. }
  1897. bt = proc { |cmd, name, type, cleanup|
  1898. tried = timeout = 0
  1899. begin
  1900. tried += 1
  1901. timeout += limit
  1902. fd = nil
  1903. data = ''
  1904. if iswin
  1905. Timeout::timeout(timeout) do
  1906. tmp = VIM::evaluate('tempname()')
  1907. system("(#{cmd}) > #{tmp}")
  1908. data = File.read(tmp).chomp
  1909. File.unlink tmp rescue nil
  1910. end
  1911. else
  1912. fd = IO.popen(cmd).extend(PlugStream)
  1913. first_line = true
  1914. log_prob = 1.0 / nthr
  1915. while line = Timeout::timeout(timeout) { fd.get_line }
  1916. data << line
  1917. log.call name, line.chomp, type if name && (first_line || rand < log_prob)
  1918. first_line = false
  1919. end
  1920. fd.close
  1921. end
  1922. [$? == 0, data.chomp]
  1923. rescue Timeout::Error, Interrupt => e
  1924. if fd && !fd.closed?
  1925. killall fd.pid
  1926. fd.close
  1927. end
  1928. cleanup.call if cleanup
  1929. if e.is_a?(Timeout::Error) && tried < tries
  1930. 3.downto(1) do |countdown|
  1931. s = countdown > 1 ? 's' : ''
  1932. log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type
  1933. sleep 1
  1934. end
  1935. log.call name, 'Retrying ...', type
  1936. retry
  1937. end
  1938. [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"]
  1939. end
  1940. }
  1941. main = Thread.current
  1942. threads = []
  1943. watcher = Thread.new {
  1944. if vim7
  1945. while VIM::evaluate('getchar(1)')
  1946. sleep 0.1
  1947. end
  1948. else
  1949. require 'io/console' # >= Ruby 1.9
  1950. nil until IO.console.getch == 3.chr
  1951. end
  1952. mtx.synchronize do
  1953. running = false
  1954. threads.each { |t| t.raise Interrupt } unless vim7
  1955. end
  1956. threads.each { |t| t.join rescue nil }
  1957. main.kill
  1958. }
  1959. refresh = Thread.new {
  1960. while true
  1961. mtx.synchronize do
  1962. break unless running
  1963. VIM::command('noautocmd normal! a')
  1964. end
  1965. sleep 0.2
  1966. end
  1967. } if VIM::evaluate('s:mac_gui') == 1
  1968. clone_opt = VIM::evaluate('s:clone_opt').join(' ')
  1969. progress = VIM::evaluate('s:progress_opt(1)')
  1970. nthr.times do
  1971. mtx.synchronize do
  1972. threads << Thread.new {
  1973. while pair = take1.call
  1974. name = pair.first
  1975. dir, uri, tag = pair.last.values_at *%w[dir uri tag]
  1976. exists = File.directory? dir
  1977. ok, result =
  1978. if exists
  1979. chdir = "#{cd} #{iswin ? dir : esc(dir)}"
  1980. ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil
  1981. current_uri = data.lines.to_a.last
  1982. if !ret
  1983. if data =~ /^Interrupted|^Timeout/
  1984. [false, data]
  1985. else
  1986. [false, [data.chomp, "PlugClean required."].join($/)]
  1987. end
  1988. elsif !compare_git_uri(current_uri, uri)
  1989. [false, ["Invalid URI: #{current_uri}",
  1990. "Expected: #{uri}",
  1991. "PlugClean required."].join($/)]
  1992. else
  1993. if pull
  1994. log.call name, 'Updating ...', :update
  1995. fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : ''
  1996. bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil
  1997. else
  1998. [true, skip]
  1999. end
  2000. end
  2001. else
  2002. d = esc dir.sub(%r{[\\/]+$}, '')
  2003. log.call name, 'Installing ...', :install
  2004. bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc {
  2005. FileUtils.rm_rf dir
  2006. }
  2007. end
  2008. mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok
  2009. log.call name, result, ok
  2010. end
  2011. } if running
  2012. end
  2013. end
  2014. threads.each { |t| t.join rescue nil }
  2015. logh.call
  2016. refresh.kill if refresh
  2017. watcher.kill
  2018. EOF
  2019. endfunction
  2020. function! s:shellesc_cmd(arg, script)
  2021. let escaped = substitute('"'.a:arg.'"', '[&|<>()@^!"]', '^&', 'g')
  2022. return substitute(escaped, '%', (a:script ? '%' : '^') . '&', 'g')
  2023. endfunction
  2024. function! s:shellesc_ps1(arg)
  2025. return "'".substitute(escape(a:arg, '\"'), "'", "''", 'g')."'"
  2026. endfunction
  2027. function! s:shellesc_sh(arg)
  2028. return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'"
  2029. endfunction
  2030. " Escape the shell argument based on the shell.
  2031. " Vim and Neovim's shellescape() are insufficient.
  2032. " 1. shellslash determines whether to use single/double quotes.
  2033. " Double-quote escaping is fragile for cmd.exe.
  2034. " 2. It does not work for powershell.
  2035. " 3. It does not work for *sh shells if the command is executed
  2036. " via cmd.exe (ie. cmd.exe /c sh -c command command_args)
  2037. " 4. It does not support batchfile syntax.
  2038. "
  2039. " Accepts an optional dictionary with the following keys:
  2040. " - shell: same as Vim/Neovim 'shell' option.
  2041. " If unset, fallback to 'cmd.exe' on Windows or 'sh'.
  2042. " - script: If truthy and shell is cmd.exe, escape for batchfile syntax.
  2043. function! plug#shellescape(arg, ...)
  2044. if a:arg =~# '^[A-Za-z0-9_/:.-]\+$'
  2045. return a:arg
  2046. endif
  2047. let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {}
  2048. let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh')
  2049. let script = get(opts, 'script', 1)
  2050. if shell =~# 'cmd\(\.exe\)\?$'
  2051. return s:shellesc_cmd(a:arg, script)
  2052. elseif s:is_powershell(shell)
  2053. return s:shellesc_ps1(a:arg)
  2054. endif
  2055. return s:shellesc_sh(a:arg)
  2056. endfunction
  2057. function! s:glob_dir(path)
  2058. return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
  2059. endfunction
  2060. function! s:progress_bar(line, bar, total)
  2061. call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']')
  2062. endfunction
  2063. function! s:compare_git_uri(a, b)
  2064. " See `git help clone'
  2065. " https:// [user@] github.com[:port] / junegunn/vim-plug [.git]
  2066. " [git@] github.com[:port] : junegunn/vim-plug [.git]
  2067. " file:// / junegunn/vim-plug [/]
  2068. " / junegunn/vim-plug [/]
  2069. let pat = '^\%(\w\+://\)\='.'\%([^@/]*@\)\='.'\([^:/]*\%(:[0-9]*\)\=\)'.'[:/]'.'\(.\{-}\)'.'\%(\.git\)\=/\?$'
  2070. let ma = matchlist(a:a, pat)
  2071. let mb = matchlist(a:b, pat)
  2072. return ma[1:2] ==# mb[1:2]
  2073. endfunction
  2074. function! s:format_message(bullet, name, message)
  2075. if a:bullet != 'x'
  2076. return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))]
  2077. else
  2078. let lines = map(s:lines(a:message), '" ".v:val')
  2079. return extend([printf('x %s:', a:name)], lines)
  2080. endif
  2081. endfunction
  2082. function! s:with_cd(cmd, dir, ...)
  2083. let script = a:0 > 0 ? a:1 : 1
  2084. let pwsh = s:is_powershell(&shell)
  2085. let cd = s:is_win && !pwsh ? 'cd /d' : 'cd'
  2086. let sep = pwsh ? ';' : '&&'
  2087. return printf('%s %s %s %s', cd, plug#shellescape(a:dir, {'script': script, 'shell': &shell}), sep, a:cmd)
  2088. endfunction
  2089. function! s:system(cmd, ...)
  2090. let batchfile = ''
  2091. try
  2092. let [sh, shellcmdflag, shrd] = s:chsh(1)
  2093. if type(a:cmd) == s:TYPE.list
  2094. " Neovim's system() supports list argument to bypass the shell
  2095. " but it cannot set the working directory for the command.
  2096. " Assume that the command does not rely on the shell.
  2097. if has('nvim') && a:0 == 0
  2098. return system(a:cmd)
  2099. endif
  2100. let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})'))
  2101. if s:is_powershell(&shell)
  2102. let cmd = '& ' . cmd
  2103. endif
  2104. else
  2105. let cmd = a:cmd
  2106. endif
  2107. if a:0 > 0
  2108. let cmd = s:with_cd(cmd, a:1, type(a:cmd) != s:TYPE.list)
  2109. endif
  2110. if s:is_win && type(a:cmd) != s:TYPE.list
  2111. let [batchfile, cmd] = s:batchfile(cmd)
  2112. endif
  2113. return system(cmd)
  2114. finally
  2115. let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
  2116. if s:is_win && filereadable(batchfile)
  2117. call delete(batchfile)
  2118. endif
  2119. endtry
  2120. endfunction
  2121. function! s:system_chomp(...)
  2122. let ret = call('s:system', a:000)
  2123. return v:shell_error ? '' : substitute(ret, '\n$', '', '')
  2124. endfunction
  2125. function! s:git_validate(spec, check_branch)
  2126. let err = ''
  2127. if isdirectory(a:spec.dir)
  2128. let result = [s:git_local_branch(a:spec.dir), s:git_origin_url(a:spec.dir)]
  2129. let remote = result[-1]
  2130. if empty(remote)
  2131. let err = join([remote, 'PlugClean required.'], "\n")
  2132. elseif !s:compare_git_uri(remote, a:spec.uri)
  2133. let err = join(['Invalid URI: '.remote,
  2134. \ 'Expected: '.a:spec.uri,
  2135. \ 'PlugClean required.'], "\n")
  2136. elseif a:check_branch && has_key(a:spec, 'commit')
  2137. let sha = s:git_revision(a:spec.dir)
  2138. if empty(sha)
  2139. let err = join(add(result, 'PlugClean required.'), "\n")
  2140. elseif !s:hash_match(sha, a:spec.commit)
  2141. let err = join([printf('Invalid HEAD (expected: %s, actual: %s)',
  2142. \ a:spec.commit[:6], sha[:6]),
  2143. \ 'PlugUpdate required.'], "\n")
  2144. endif
  2145. elseif a:check_branch
  2146. let current_branch = result[0]
  2147. " Check tag
  2148. let origin_branch = s:git_origin_branch(a:spec)
  2149. if has_key(a:spec, 'tag')
  2150. let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir)
  2151. if a:spec.tag !=# tag && a:spec.tag !~ '\*'
  2152. let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.',
  2153. \ (empty(tag) ? 'N/A' : tag), a:spec.tag)
  2154. endif
  2155. " Check branch
  2156. elseif origin_branch !=# current_branch
  2157. let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.',
  2158. \ current_branch, origin_branch)
  2159. endif
  2160. if empty(err)
  2161. let ahead_behind = split(s:lastline(s:system([
  2162. \ 'git', 'rev-list', '--count', '--left-right',
  2163. \ printf('HEAD...origin/%s', origin_branch)
  2164. \ ], a:spec.dir)), '\t')
  2165. if v:shell_error || len(ahead_behind) != 2
  2166. let err = "Failed to compare with the origin. The default branch might have changed.\nPlugClean required."
  2167. else
  2168. let [ahead, behind] = ahead_behind
  2169. if ahead && behind
  2170. " Only mention PlugClean if diverged, otherwise it's likely to be
  2171. " pushable (and probably not that messed up).
  2172. let err = printf(
  2173. \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n"
  2174. \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', origin_branch, ahead, behind)
  2175. elseif ahead
  2176. let err = printf("Ahead of origin/%s by %d commit(s).\n"
  2177. \ .'Cannot update until local changes are pushed.',
  2178. \ origin_branch, ahead)
  2179. endif
  2180. endif
  2181. endif
  2182. endif
  2183. else
  2184. let err = 'Not found'
  2185. endif
  2186. return [err, err =~# 'PlugClean']
  2187. endfunction
  2188. function! s:rm_rf(dir)
  2189. if isdirectory(a:dir)
  2190. return s:system(s:is_win
  2191. \ ? 'rmdir /S /Q '.plug#shellescape(a:dir)
  2192. \ : ['rm', '-rf', a:dir])
  2193. endif
  2194. endfunction
  2195. function! s:clean(force)
  2196. call s:prepare()
  2197. call append(0, 'Searching for invalid plugins in '.g:plug_home)
  2198. call append(1, '')
  2199. " List of valid directories
  2200. let dirs = []
  2201. let errs = {}
  2202. let [cnt, total] = [0, len(g:plugs)]
  2203. for [name, spec] in items(g:plugs)
  2204. if !s:is_managed(name) || get(spec, 'frozen', 0)
  2205. call add(dirs, spec.dir)
  2206. else
  2207. let [err, clean] = s:git_validate(spec, 1)
  2208. if clean
  2209. let errs[spec.dir] = s:lines(err)[0]
  2210. else
  2211. call add(dirs, spec.dir)
  2212. endif
  2213. endif
  2214. let cnt += 1
  2215. call s:progress_bar(2, repeat('=', cnt), total)
  2216. normal! 2G
  2217. redraw
  2218. endfor
  2219. let allowed = {}
  2220. for dir in dirs
  2221. let allowed[s:dirpath(s:plug_fnamemodify(dir, ':h:h'))] = 1
  2222. let allowed[dir] = 1
  2223. for child in s:glob_dir(dir)
  2224. let allowed[child] = 1
  2225. endfor
  2226. endfor
  2227. let todo = []
  2228. let found = sort(s:glob_dir(g:plug_home))
  2229. while !empty(found)
  2230. let f = remove(found, 0)
  2231. if !has_key(allowed, f) && isdirectory(f)
  2232. call add(todo, f)
  2233. call append(line('$'), '- ' . f)
  2234. if has_key(errs, f)
  2235. call append(line('$'), ' ' . errs[f])
  2236. endif
  2237. let found = filter(found, 'stridx(v:val, f) != 0')
  2238. end
  2239. endwhile
  2240. 4
  2241. redraw
  2242. if empty(todo)
  2243. call append(line('$'), 'Already clean.')
  2244. else
  2245. let s:clean_count = 0
  2246. call append(3, ['Directories to delete:', ''])
  2247. redraw!
  2248. if a:force || s:ask_no_interrupt('Delete all directories?')
  2249. call s:delete([6, line('$')], 1)
  2250. else
  2251. call setline(4, 'Cancelled.')
  2252. nnoremap <silent> <buffer> d :set opfunc=<sid>delete_op<cr>g@
  2253. nmap <silent> <buffer> dd d_
  2254. xnoremap <silent> <buffer> d :<c-u>call <sid>delete_op(visualmode(), 1)<cr>
  2255. echo 'Delete the lines (d{motion}) to delete the corresponding directories'
  2256. endif
  2257. endif
  2258. 4
  2259. setlocal nomodifiable
  2260. endfunction
  2261. function! s:delete_op(type, ...)
  2262. call s:delete(a:0 ? [line("'<"), line("'>")] : [line("'["), line("']")], 0)
  2263. endfunction
  2264. function! s:delete(range, force)
  2265. let [l1, l2] = a:range
  2266. let force = a:force
  2267. let err_count = 0
  2268. while l1 <= l2
  2269. let line = getline(l1)
  2270. if line =~ '^- ' && isdirectory(line[2:])
  2271. execute l1
  2272. redraw!
  2273. let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1)
  2274. let force = force || answer > 1
  2275. if answer
  2276. let err = s:rm_rf(line[2:])
  2277. setlocal modifiable
  2278. if empty(err)
  2279. call setline(l1, '~'.line[1:])
  2280. let s:clean_count += 1
  2281. else
  2282. delete _
  2283. call append(l1 - 1, s:format_message('x', line[1:], err))
  2284. let l2 += len(s:lines(err))
  2285. let err_count += 1
  2286. endif
  2287. let msg = printf('Removed %d directories.', s:clean_count)
  2288. if err_count > 0
  2289. let msg .= printf(' Failed to remove %d directories.', err_count)
  2290. endif
  2291. call setline(4, msg)
  2292. setlocal nomodifiable
  2293. endif
  2294. endif
  2295. let l1 += 1
  2296. endwhile
  2297. endfunction
  2298. function! s:upgrade()
  2299. echo 'Downloading the latest version of vim-plug'
  2300. redraw
  2301. let tmp = s:plug_tempname()
  2302. let new = tmp . '/plug.vim'
  2303. try
  2304. let out = s:system(['git', 'clone', '--depth', '1', s:plug_src, tmp])
  2305. if v:shell_error
  2306. return s:err('Error upgrading vim-plug: '. out)
  2307. endif
  2308. if readfile(s:me) ==# readfile(new)
  2309. echo 'vim-plug is already up-to-date'
  2310. return 0
  2311. else
  2312. call rename(s:me, s:me . '.old')
  2313. call rename(new, s:me)
  2314. unlet g:loaded_plug
  2315. echo 'vim-plug has been upgraded'
  2316. return 1
  2317. endif
  2318. finally
  2319. silent! call s:rm_rf(tmp)
  2320. endtry
  2321. endfunction
  2322. function! s:upgrade_specs()
  2323. for spec in values(g:plugs)
  2324. let spec.frozen = get(spec, 'frozen', 0)
  2325. endfor
  2326. endfunction
  2327. function! s:status()
  2328. call s:prepare()
  2329. call append(0, 'Checking plugins')
  2330. call append(1, '')
  2331. let ecnt = 0
  2332. let unloaded = 0
  2333. let [cnt, total] = [0, len(g:plugs)]
  2334. for [name, spec] in items(g:plugs)
  2335. let is_dir = isdirectory(spec.dir)
  2336. if has_key(spec, 'uri')
  2337. if is_dir
  2338. let [err, _] = s:git_validate(spec, 1)
  2339. let [valid, msg] = [empty(err), empty(err) ? 'OK' : err]
  2340. else
  2341. let [valid, msg] = [0, 'Not found. Try PlugInstall.']
  2342. endif
  2343. else
  2344. if is_dir
  2345. let [valid, msg] = [1, 'OK']
  2346. else
  2347. let [valid, msg] = [0, 'Not found.']
  2348. endif
  2349. endif
  2350. let cnt += 1
  2351. let ecnt += !valid
  2352. " `s:loaded` entry can be missing if PlugUpgraded
  2353. if is_dir && get(s:loaded, name, -1) == 0
  2354. let unloaded = 1
  2355. let msg .= ' (not loaded)'
  2356. endif
  2357. call s:progress_bar(2, repeat('=', cnt), total)
  2358. call append(3, s:format_message(valid ? '-' : 'x', name, msg))
  2359. normal! 2G
  2360. redraw
  2361. endfor
  2362. call setline(1, 'Finished. '.ecnt.' error(s).')
  2363. normal! gg
  2364. setlocal nomodifiable
  2365. if unloaded
  2366. echo "Press 'L' on each line to load plugin, or 'U' to update"
  2367. nnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr>
  2368. xnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr>
  2369. end
  2370. endfunction
  2371. function! s:extract_name(str, prefix, suffix)
  2372. return matchstr(a:str, '^'.a:prefix.' \zs[^:]\+\ze:.*'.a:suffix.'$')
  2373. endfunction
  2374. function! s:status_load(lnum)
  2375. let line = getline(a:lnum)
  2376. let name = s:extract_name(line, '-', '(not loaded)')
  2377. if !empty(name)
  2378. call plug#load(name)
  2379. setlocal modifiable
  2380. call setline(a:lnum, substitute(line, ' (not loaded)$', '', ''))
  2381. setlocal nomodifiable
  2382. endif
  2383. endfunction
  2384. function! s:status_update() range
  2385. let lines = getline(a:firstline, a:lastline)
  2386. let names = filter(map(lines, 's:extract_name(v:val, "[x-]", "")'), '!empty(v:val)')
  2387. if !empty(names)
  2388. echo
  2389. execute 'PlugUpdate' join(names)
  2390. endif
  2391. endfunction
  2392. function! s:is_preview_window_open()
  2393. silent! wincmd P
  2394. if &previewwindow
  2395. wincmd p
  2396. return 1
  2397. endif
  2398. endfunction
  2399. function! s:find_name(lnum)
  2400. for lnum in reverse(range(1, a:lnum))
  2401. let line = getline(lnum)
  2402. if empty(line)
  2403. return ''
  2404. endif
  2405. let name = s:extract_name(line, '-', '')
  2406. if !empty(name)
  2407. return name
  2408. endif
  2409. endfor
  2410. return ''
  2411. endfunction
  2412. function! s:preview_commit()
  2413. if b:plug_preview < 0
  2414. let b:plug_preview = !s:is_preview_window_open()
  2415. endif
  2416. let sha = matchstr(getline('.'), '^ \X*\zs[0-9a-f]\{7,9}')
  2417. if empty(sha)
  2418. let name = matchstr(getline('.'), '^- \zs[^:]*\ze:$')
  2419. if empty(name)
  2420. return
  2421. endif
  2422. let title = 'HEAD@{1}..'
  2423. let command = 'git diff --no-color HEAD@{1}'
  2424. else
  2425. let title = sha
  2426. let command = 'git show --no-color --pretty=medium '.sha
  2427. let name = s:find_name(line('.'))
  2428. endif
  2429. if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir)
  2430. return
  2431. endif
  2432. if !s:is_preview_window_open()
  2433. execute get(g:, 'plug_pwindow', 'vertical rightbelow new')
  2434. execute 'e' title
  2435. else
  2436. execute 'pedit' title
  2437. wincmd P
  2438. endif
  2439. setlocal previewwindow filetype=git buftype=nofile bufhidden=wipe nobuflisted modifiable
  2440. let batchfile = ''
  2441. try
  2442. let [sh, shellcmdflag, shrd] = s:chsh(1)
  2443. let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && '.command
  2444. if s:is_win
  2445. let [batchfile, cmd] = s:batchfile(cmd)
  2446. endif
  2447. execute 'silent %!' cmd
  2448. finally
  2449. let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
  2450. if s:is_win && filereadable(batchfile)
  2451. call delete(batchfile)
  2452. endif
  2453. endtry
  2454. setlocal nomodifiable
  2455. nnoremap <silent> <buffer> q :q<cr>
  2456. wincmd p
  2457. endfunction
  2458. function! s:section(flags)
  2459. call search('\(^[x-] \)\@<=[^:]\+:', a:flags)
  2460. endfunction
  2461. function! s:format_git_log(line)
  2462. let indent = ' '
  2463. let tokens = split(a:line, nr2char(1))
  2464. if len(tokens) != 5
  2465. return indent.substitute(a:line, '\s*$', '', '')
  2466. endif
  2467. let [graph, sha, refs, subject, date] = tokens
  2468. let tag = matchstr(refs, 'tag: [^,)]\+')
  2469. let tag = empty(tag) ? ' ' : ' ('.tag.') '
  2470. return printf('%s%s%s%s%s (%s)', indent, graph, sha, tag, subject, date)
  2471. endfunction
  2472. function! s:append_ul(lnum, text)
  2473. call append(a:lnum, ['', a:text, repeat('-', len(a:text))])
  2474. endfunction
  2475. function! s:diff()
  2476. call s:prepare()
  2477. call append(0, ['Collecting changes ...', ''])
  2478. let cnts = [0, 0]
  2479. let bar = ''
  2480. let total = filter(copy(g:plugs), 's:is_managed(v:key) && isdirectory(v:val.dir)')
  2481. call s:progress_bar(2, bar, len(total))
  2482. for origin in [1, 0]
  2483. let plugs = reverse(sort(items(filter(copy(total), (origin ? '' : '!').'(has_key(v:val, "commit") || has_key(v:val, "tag"))'))))
  2484. if empty(plugs)
  2485. continue
  2486. endif
  2487. call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:')
  2488. for [k, v] in plugs
  2489. let branch = s:git_origin_branch(v)
  2490. if len(branch)
  2491. let range = origin ? '..origin/'.branch : 'HEAD@{1}..'
  2492. let cmd = ['git', 'log', '--graph', '--color=never']
  2493. if s:git_version_requirement(2, 10, 0)
  2494. call add(cmd, '--no-show-signature')
  2495. endif
  2496. call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range])
  2497. if has_key(v, 'rtp')
  2498. call extend(cmd, ['--', v.rtp])
  2499. endif
  2500. let diff = s:system_chomp(cmd, v.dir)
  2501. if !empty(diff)
  2502. let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : ''
  2503. call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)')))
  2504. let cnts[origin] += 1
  2505. endif
  2506. endif
  2507. let bar .= '='
  2508. call s:progress_bar(2, bar, len(total))
  2509. normal! 2G
  2510. redraw
  2511. endfor
  2512. if !cnts[origin]
  2513. call append(5, ['', 'N/A'])
  2514. endif
  2515. endfor
  2516. call setline(1, printf('%d plugin(s) updated.', cnts[0])
  2517. \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : ''))
  2518. if cnts[0] || cnts[1]
  2519. nnoremap <silent> <buffer> <plug>(plug-preview) :silent! call <SID>preview_commit()<cr>
  2520. if empty(maparg("\<cr>", 'n'))
  2521. nmap <buffer> <cr> <plug>(plug-preview)
  2522. endif
  2523. if empty(maparg('o', 'n'))
  2524. nmap <buffer> o <plug>(plug-preview)
  2525. endif
  2526. endif
  2527. if cnts[0]
  2528. nnoremap <silent> <buffer> X :call <SID>revert()<cr>
  2529. echo "Press 'X' on each block to revert the update"
  2530. endif
  2531. normal! gg
  2532. setlocal nomodifiable
  2533. endfunction
  2534. function! s:revert()
  2535. if search('^Pending updates', 'bnW')
  2536. return
  2537. endif
  2538. let name = s:find_name(line('.'))
  2539. if empty(name) || !has_key(g:plugs, name) ||
  2540. \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y'
  2541. return
  2542. endif
  2543. call s:system('git reset --hard HEAD@{1} && git checkout '.plug#shellescape(g:plugs[name].branch).' --', g:plugs[name].dir)
  2544. setlocal modifiable
  2545. normal! "_dap
  2546. setlocal nomodifiable
  2547. echo 'Reverted'
  2548. endfunction
  2549. function! s:snapshot(force, ...) abort
  2550. call s:prepare()
  2551. setf vim
  2552. call append(0, ['" Generated by vim-plug',
  2553. \ '" '.strftime("%c"),
  2554. \ '" :source this file in vim to restore the snapshot',
  2555. \ '" or execute: vim -S snapshot.vim',
  2556. \ '', '', 'PlugUpdate!'])
  2557. 1
  2558. let anchor = line('$') - 3
  2559. let names = sort(keys(filter(copy(g:plugs),
  2560. \'has_key(v:val, "uri") && isdirectory(v:val.dir)')))
  2561. for name in reverse(names)
  2562. let sha = has_key(g:plugs[name], 'commit') ? g:plugs[name].commit : s:git_revision(g:plugs[name].dir)
  2563. if !empty(sha)
  2564. call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha))
  2565. redraw
  2566. endif
  2567. endfor
  2568. if a:0 > 0
  2569. let fn = s:plug_expand(a:1)
  2570. if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?'))
  2571. return
  2572. endif
  2573. call writefile(getline(1, '$'), fn)
  2574. echo 'Saved as '.a:1
  2575. silent execute 'e' s:esc(fn)
  2576. setf vim
  2577. endif
  2578. endfunction
  2579. function! s:split_rtp()
  2580. return split(&rtp, '\\\@<!,')
  2581. endfunction
  2582. let s:first_rtp = s:escrtp(get(s:split_rtp(), 0, ''))
  2583. let s:last_rtp = s:escrtp(get(s:split_rtp(), -1, ''))
  2584. if exists('g:plugs')
  2585. let g:plugs_order = get(g:, 'plugs_order', keys(g:plugs))
  2586. call s:upgrade_specs()
  2587. call s:define_commands()
  2588. endif
  2589. let &cpo = s:cpo_save
  2590. unlet s:cpo_save