plug.vim 69 KB

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