plug.vim 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250
  1. " vim-plug: Vim plugin manager
  2. " ============================
  3. "
  4. " Download plug.vim and put it in ~/.vim/autoload
  5. "
  6. " mkdir -p ~/.vim/autoload
  7. " curl -fLo ~/.vim/autoload/plug.vim \
  8. " https://raw.github.com/junegunn/vim-plug/master/plug.vim
  9. "
  10. " Edit your .vimrc
  11. "
  12. " call plug#begin('~/.vim/plugged')
  13. "
  14. " " Make sure you use single quotes
  15. " Plug 'junegunn/seoul256.vim'
  16. " Plug 'junegunn/vim-easy-align'
  17. "
  18. " " On-demand loading
  19. " Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' }
  20. " Plug 'tpope/vim-fireplace', { 'for': 'clojure' }
  21. "
  22. " " Using git URL
  23. " Plug 'https://github.com/junegunn/vim-github-dashboard.git'
  24. "
  25. " " Plugin options
  26. " Plug 'nsf/gocode', { 'tag': 'go.weekly.2012-03-13', 'rtp': 'vim' }
  27. "
  28. " " Plugin outside ~/.vim/plugged with post-update hook
  29. " Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': 'yes \| ./install' }
  30. "
  31. " " Unmanaged plugin (manually installed and updated)
  32. " Plug '~/my-prototype-plugin'
  33. "
  34. " call plug#end()
  35. "
  36. " Then reload .vimrc and :PlugInstall to install plugins.
  37. " Visit https://github.com/junegunn/vim-plug for more information.
  38. "
  39. "
  40. " Copyright (c) 2014 Junegunn Choi
  41. "
  42. " MIT License
  43. "
  44. " Permission is hereby granted, free of charge, to any person obtaining
  45. " a copy of this software and associated documentation files (the
  46. " "Software"), to deal in the Software without restriction, including
  47. " without limitation the rights to use, copy, modify, merge, publish,
  48. " distribute, sublicense, and/or sell copies of the Software, and to
  49. " permit persons to whom the Software is furnished to do so, subject to
  50. " the following conditions:
  51. "
  52. " The above copyright notice and this permission notice shall be
  53. " included in all copies or substantial portions of the Software.
  54. "
  55. " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  56. " EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  57. " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  58. " NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  59. " LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  60. " OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  61. " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  62. if exists('g:loaded_plug')
  63. finish
  64. endif
  65. let g:loaded_plug = 1
  66. let s:cpo_save = &cpo
  67. set cpo&vim
  68. let s:plug_source = 'https://raw.github.com/junegunn/vim-plug/master/plug.vim'
  69. let s:plug_buf = get(s:, 'plug_buf', -1)
  70. let s:mac_gui = has('gui_macvim') && has('gui_running')
  71. let s:is_win = has('win32') || has('win64')
  72. let s:me = expand('<sfile>:p')
  73. let s:base_spec = { 'branch': 'master', 'frozen': 0 }
  74. let s:TYPE = {
  75. \ 'string': type(''),
  76. \ 'list': type([]),
  77. \ 'dict': type({}),
  78. \ 'funcref': type(function('call'))
  79. \ }
  80. let s:loaded = get(s:, 'loaded', {})
  81. function! plug#begin(...)
  82. if a:0 > 0
  83. let home = s:path(fnamemodify(a:1, ':p'))
  84. elseif exists('g:plug_home')
  85. let home = s:path(g:plug_home)
  86. elseif !empty(&rtp)
  87. let home = s:path(split(&rtp, ',')[0]) . '/plugged'
  88. else
  89. return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.')
  90. endif
  91. let g:plug_home = home
  92. let g:plugs = {}
  93. " we want to keep track of the order plugins where registered.
  94. let g:plugs_order = []
  95. call s:define_commands()
  96. return 1
  97. endfunction
  98. function! s:define_commands()
  99. command! -nargs=+ -bar Plug call s:add(<args>)
  100. if !executable('git')
  101. return s:err('`git` executable not found. vim-plug requires git.')
  102. endif
  103. command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install('<bang>' == '!', <f-args>)
  104. command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update('<bang>' == '!', <f-args>)
  105. command! -nargs=0 -bar -bang PlugClean call s:clean('<bang>' == '!')
  106. command! -nargs=0 -bar PlugUpgrade if s:upgrade() | execute 'source '. s:me | endif
  107. command! -nargs=0 -bar PlugStatus call s:status()
  108. command! -nargs=0 -bar PlugDiff call s:diff()
  109. endfunction
  110. function! s:to_a(v)
  111. return type(a:v) == s:TYPE.list ? a:v : [a:v]
  112. endfunction
  113. function! s:source(from, ...)
  114. for pattern in a:000
  115. for vim in split(globpath(a:from, pattern), '\n')
  116. execute 'source '.vim
  117. endfor
  118. endfor
  119. endfunction
  120. function! plug#end()
  121. let reload = !has('vim_starting')
  122. if !exists('g:plugs')
  123. return s:err('Call plug#begin() first')
  124. endif
  125. if exists('#PlugLOD')
  126. augroup PlugLOD
  127. autocmd!
  128. augroup END
  129. augroup! PlugLOD
  130. endif
  131. let lod = {}
  132. filetype off
  133. " we want to make sure the plugin directories are added to rtp in the same
  134. " order that they are registered with the Plug command. since the s:add_rtp
  135. " function uses ^= to add plugin directories to the front of the rtp, we
  136. " need to loop through the plugins in reverse
  137. for name in reverse(copy(g:plugs_order))
  138. let plug = g:plugs[name]
  139. if get(s:loaded, plug.dir, 0) || !has_key(plug, 'on') && !has_key(plug, 'for')
  140. let rtp = s:add_rtp(plug)
  141. if reload
  142. call s:source(rtp, 'plugin/**/*.vim', 'after/plugin/**/*.vim')
  143. endif
  144. continue
  145. endif
  146. if has_key(plug, 'on')
  147. for cmd in s:to_a(plug.on)
  148. if cmd =~ '^<Plug>.\+'
  149. if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i'))
  150. for [mode, map_prefix, key_prefix] in
  151. \ [['i', '<C-O>', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']]
  152. execute printf(
  153. \ '%snoremap <silent> %s %s:<C-U>call <SID>lod_map(%s, %s, "%s")<CR>',
  154. \ mode, cmd, map_prefix, string(cmd), string(name), key_prefix)
  155. endfor
  156. endif
  157. elseif !exists(':'.cmd)
  158. execute printf(
  159. \ 'command! -nargs=* -range -bang %s call s:lod_cmd(%s, "<bang>", <line1>, <line2>, <q-args>, %s)',
  160. \ cmd, string(cmd), string(name))
  161. endif
  162. endfor
  163. endif
  164. if has_key(plug, 'for')
  165. let types = s:to_a(plug.for)
  166. if !empty(types)
  167. call s:source(s:rtp(plug), 'ftdetect/**/*.vim', 'after/ftdetect/**/*.vim')
  168. endif
  169. for key in types
  170. if !has_key(lod, key)
  171. let lod[key] = []
  172. endif
  173. call add(lod[key], name)
  174. endfor
  175. endif
  176. endfor
  177. for [key, names] in items(lod)
  178. augroup PlugLOD
  179. execute printf('autocmd FileType %s call <SID>lod_ft(%s, %s)',
  180. \ key, string(key), string(reverse(names)))
  181. augroup END
  182. endfor
  183. call s:reorg_rtp()
  184. filetype plugin indent on
  185. syntax on
  186. endfunction
  187. function! s:trim(str)
  188. return substitute(a:str, '[\/]\+$', '', '')
  189. endfunction
  190. if s:is_win
  191. function! s:rtp(spec)
  192. return s:path(a:spec.dir . get(a:spec, 'rtp', ''))
  193. endfunction
  194. function! s:path(path)
  195. return s:trim(substitute(a:path, '/', '\', 'g'))
  196. endfunction
  197. function! s:dirpath(path)
  198. return s:path(a:path) . '\'
  199. endfunction
  200. function! s:is_local_plug(repo)
  201. return a:repo =~? '^[a-z]:'
  202. endfunction
  203. else
  204. function! s:rtp(spec)
  205. return s:dirpath(a:spec.dir . get(a:spec, 'rtp', ''))
  206. endfunction
  207. function! s:path(path)
  208. return s:trim(a:path)
  209. endfunction
  210. function! s:dirpath(path)
  211. return substitute(a:path, '[/\\]*$', '/', '')
  212. endfunction
  213. function! s:is_local_plug(repo)
  214. return a:repo[0] =~ '[/$~]'
  215. endfunction
  216. endif
  217. function! s:err(msg)
  218. echohl ErrorMsg
  219. echom a:msg
  220. echohl None
  221. return 0
  222. endfunction
  223. function! s:esc(path)
  224. return substitute(a:path, ' ', '\\ ', 'g')
  225. endfunction
  226. function! s:add_rtp(plug)
  227. let rtp = s:rtp(a:plug)
  228. execute 'set rtp^='.s:esc(rtp)
  229. let after = globpath(rtp, 'after')
  230. if isdirectory(after)
  231. execute 'set rtp+='.s:esc(after)
  232. endif
  233. let s:loaded[a:plug.dir] = 1
  234. return rtp
  235. endfunction
  236. function! s:reorg_rtp()
  237. if !empty(s:first_rtp)
  238. execute 'set rtp-='.s:first_rtp
  239. execute 'set rtp^='.s:first_rtp
  240. endif
  241. if s:last_rtp !=# s:first_rtp
  242. execute 'set rtp-='.s:last_rtp
  243. execute 'set rtp+='.s:last_rtp
  244. endif
  245. endfunction
  246. function! plug#load(...)
  247. if a:0 == 0
  248. return s:err('Argument missing: plugin name(s) required')
  249. endif
  250. if !exists('g:plugs')
  251. return s:err('plug#begin was not called')
  252. endif
  253. let unknowns = filter(copy(a:000), '!has_key(g:plugs, v:val)')
  254. if !empty(unknowns)
  255. let s = len(unknowns) > 1 ? 's' : ''
  256. return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', ')))
  257. end
  258. for name in a:000
  259. call s:lod(g:plugs[name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
  260. endfor
  261. call s:reorg_rtp()
  262. silent! doautocmd BufRead
  263. return 1
  264. endfunction
  265. function! s:lod(plug, types)
  266. let rtp = s:add_rtp(a:plug)
  267. for dir in a:types
  268. call s:source(rtp, dir.'/**/*.vim')
  269. endfor
  270. endfunction
  271. function! s:lod_ft(pat, names)
  272. for name in a:names
  273. call s:lod(g:plugs[name], ['plugin', 'after/plugin'])
  274. endfor
  275. call s:reorg_rtp()
  276. execute 'autocmd! PlugLOD FileType ' . a:pat
  277. silent! doautocmd filetypeplugin FileType
  278. silent! doautocmd filetypeindent FileType
  279. endfunction
  280. function! s:lod_cmd(cmd, bang, l1, l2, args, name)
  281. execute 'delc '.a:cmd
  282. call s:lod(g:plugs[a:name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
  283. call s:reorg_rtp()
  284. execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args)
  285. endfunction
  286. function! s:lod_map(map, name, prefix)
  287. execute 'unmap '.a:map
  288. execute 'iunmap '.a:map
  289. call s:lod(g:plugs[a:name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
  290. call s:reorg_rtp()
  291. let extra = ''
  292. while 1
  293. let c = getchar(0)
  294. if c == 0
  295. break
  296. endif
  297. let extra .= nr2char(c)
  298. endwhile
  299. call feedkeys(a:prefix . substitute(a:map, '^<Plug>', "\<Plug>", '') . extra)
  300. endfunction
  301. function! s:add(repo, ...)
  302. if a:0 > 1
  303. return s:err('Invalid number of arguments (1..2)')
  304. endif
  305. try
  306. let repo = s:trim(a:repo)
  307. let name = fnamemodify(repo, ':t:s?\.git$??')
  308. let spec = extend(s:infer_properties(name, repo),
  309. \ a:0 == 1 ? s:parse_options(a:1) : s:base_spec)
  310. let g:plugs[name] = spec
  311. let g:plugs_order += [name]
  312. let s:loaded[spec.dir] = 0
  313. catch
  314. return s:err(v:exception)
  315. endtry
  316. endfunction
  317. function! s:parse_options(arg)
  318. let opts = copy(s:base_spec)
  319. let type = type(a:arg)
  320. if type == s:TYPE.string
  321. let opts.branch = a:arg
  322. elseif type == s:TYPE.dict
  323. call extend(opts, a:arg)
  324. if has_key(opts, 'tag')
  325. let opts.branch = remove(opts, 'tag')
  326. endif
  327. if has_key(opts, 'dir')
  328. let opts.dir = s:dirpath(expand(opts.dir))
  329. endif
  330. else
  331. throw 'Invalid argument type (expected: string or dictionary)'
  332. endif
  333. return opts
  334. endfunction
  335. function! s:infer_properties(name, repo)
  336. let repo = a:repo
  337. if s:is_local_plug(repo)
  338. return { 'dir': s:dirpath(expand(repo)) }
  339. else
  340. if repo =~ ':'
  341. let uri = repo
  342. else
  343. if repo !~ '/'
  344. let repo = 'vim-scripts/'. repo
  345. endif
  346. let uri = 'https://git::@github.com/' . repo . '.git'
  347. endif
  348. let dir = s:dirpath( fnamemodify(join([g:plug_home, a:name], '/'), ':p') )
  349. return { 'dir': dir, 'uri': uri }
  350. endif
  351. endfunction
  352. function! s:install(force, ...)
  353. call s:update_impl(0, a:force, a:000)
  354. endfunction
  355. function! s:update(force, ...)
  356. call s:update_impl(1, a:force, a:000)
  357. endfunction
  358. function! plug#helptags()
  359. if !exists('g:plugs')
  360. return s:err('plug#begin was not called')
  361. endif
  362. for spec in values(g:plugs)
  363. let docd = join([spec.dir, 'doc'], '/')
  364. if isdirectory(docd)
  365. silent! execute 'helptags '. s:esc(docd)
  366. endif
  367. endfor
  368. return 1
  369. endfunction
  370. function! s:syntax()
  371. syntax clear
  372. syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber
  373. syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX
  374. syn match plugNumber /[0-9]\+[0-9.]*/ contained
  375. syn match plugBracket /[[\]]/ contained
  376. syn match plugX /x/ contained
  377. syn match plugDash /^-/
  378. syn match plugPlus /^+/
  379. syn match plugStar /^*/
  380. syn match plugMessage /\(^- \)\@<=.*/
  381. syn match plugName /\(^- \)\@<=[^ ]*:/
  382. syn match plugInstall /\(^+ \)\@<=[^:]*/
  383. syn match plugUpdate /\(^* \)\@<=[^:]*/
  384. syn match plugCommit /^ [0-9a-z]\{7} .*/ contains=plugRelDate,plugSha
  385. syn match plugSha /\(^ \)\@<=[0-9a-z]\{7}/ contained
  386. syn match plugRelDate /([^)]*)$/ contained
  387. syn match plugNotLoaded /(not loaded)$/
  388. syn match plugError /^x.*/
  389. syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
  390. hi def link plug1 Title
  391. hi def link plug2 Repeat
  392. hi def link plugX Exception
  393. hi def link plugBracket Structure
  394. hi def link plugNumber Number
  395. hi def link plugDash Special
  396. hi def link plugPlus Constant
  397. hi def link plugStar Boolean
  398. hi def link plugMessage Function
  399. hi def link plugName Label
  400. hi def link plugInstall Function
  401. hi def link plugUpdate Type
  402. hi def link plugError Error
  403. hi def link plugRelDate Comment
  404. hi def link plugSha Identifier
  405. hi def link plugNotLoaded Comment
  406. endfunction
  407. function! s:lpad(str, len)
  408. return a:str . repeat(' ', a:len - len(a:str))
  409. endfunction
  410. function! s:lastline(msg)
  411. let lines = split(a:msg, '\n')
  412. return get(lines, -1, '')
  413. endfunction
  414. function! s:prepare()
  415. if bufexists(s:plug_buf)
  416. let winnr = bufwinnr(s:plug_buf)
  417. if winnr < 0
  418. vertical topleft new
  419. execute 'buffer ' . s:plug_buf
  420. else
  421. execute winnr . 'wincmd w'
  422. endif
  423. setlocal modifiable
  424. silent %d _
  425. else
  426. vertical topleft new
  427. nnoremap <silent> <buffer> q :if b:plug_preview==1<bar>pc<bar>endif<bar>q<cr>
  428. nnoremap <silent> <buffer> R :silent! call <SID>retry()<cr>
  429. nnoremap <silent> <buffer> D :PlugDiff<cr>
  430. nnoremap <silent> <buffer> S :PlugStatus<cr>
  431. nnoremap <silent> <buffer> ]] :silent! call <SID>section('')<cr>
  432. nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr>
  433. let b:plug_preview = -1
  434. let s:plug_buf = winbufnr(0)
  435. call s:assign_name()
  436. endif
  437. silent! unmap <buffer> <cr>
  438. silent! unmap <buffer> L
  439. silent! unmap <buffer> X
  440. setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline modifiable
  441. setf vim-plug
  442. call s:syntax()
  443. endfunction
  444. function! s:assign_name()
  445. " Assign buffer name
  446. let prefix = '[Plugins]'
  447. let name = prefix
  448. let idx = 2
  449. while bufexists(name)
  450. let name = printf('%s (%s)', prefix, idx)
  451. let idx = idx + 1
  452. endwhile
  453. silent! execute 'f '.fnameescape(name)
  454. endfunction
  455. function! s:do(pull, force, todo)
  456. for [name, spec] in items(a:todo)
  457. if !isdirectory(spec.dir)
  458. continue
  459. endif
  460. execute 'cd '.s:esc(spec.dir)
  461. let installed = has_key(s:prev_update.new, name)
  462. let updated = installed ? 0 :
  463. \ (a:pull && !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"')))
  464. if a:force || installed || updated
  465. call append(3, '- Post-update hook for '. name .' ... ')
  466. let type = type(spec.do)
  467. if type == s:TYPE.string
  468. try
  469. " FIXME: Escaping is incomplete. We could use shellescape with eval,
  470. " but it won't work on Windows.
  471. let g:_plug_do = '!'.escape(spec.do, '#!%')
  472. execute "normal! :execute g:_plug_do\<cr>\<cr>"
  473. finally
  474. let result = v:shell_error ? ('Exit status: '.v:shell_error) : 'Done!'
  475. unlet g:_plug_do
  476. endtry
  477. elseif type == s:TYPE.funcref
  478. try
  479. let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged')
  480. call spec.do({ 'name': name, 'status': status, 'force': a:force })
  481. let result = 'Done!'
  482. catch
  483. let result = 'Error: ' . v:exception
  484. endtry
  485. else
  486. let result = 'Error: Invalid type!'
  487. endif
  488. call setline(4, getline(4) . result)
  489. endif
  490. cd -
  491. endfor
  492. endfunction
  493. function! s:finish(pull)
  494. call append(3, '- Finishing ... ')
  495. redraw
  496. call plug#helptags()
  497. call plug#end()
  498. call setline(4, getline(4) . 'Done!')
  499. normal! gg
  500. call s:syntax()
  501. redraw
  502. let msgs = []
  503. if !empty(s:prev_update.errors)
  504. call add(msgs, "Press 'R' to retry.")
  505. endif
  506. if a:pull
  507. call add(msgs, "Press 'D' to see the updated changes.")
  508. endif
  509. echo join(msgs, ' ')
  510. endfunction
  511. function! s:retry()
  512. if empty(s:prev_update.errors)
  513. return
  514. endif
  515. call s:update_impl(s:prev_update.pull, s:prev_update.force,
  516. \ extend(copy(s:prev_update.errors), [s:prev_update.threads]))
  517. endfunction
  518. function! s:is_managed(name)
  519. return has_key(g:plugs[a:name], 'uri')
  520. endfunction
  521. function! s:names(...)
  522. return filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')
  523. endfunction
  524. function! s:update_impl(pull, force, args) abort
  525. let st = reltime()
  526. let args = copy(a:args)
  527. let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
  528. \ remove(args, -1) : get(g:, 'plug_threads', 16)
  529. let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
  530. let todo = empty(args) ? filter(managed, '!v:val.frozen') :
  531. \ filter(managed, 'index(args, v:key) >= 0')
  532. if empty(todo)
  533. echohl WarningMsg
  534. echo 'No plugin to '. (a:pull ? 'update' : 'install') . '.'
  535. echohl None
  536. return
  537. endif
  538. if !isdirectory(g:plug_home)
  539. try
  540. call mkdir(g:plug_home, 'p')
  541. catch
  542. return s:err(printf('Invalid plug directory: %s.'
  543. \ 'Try to call plug#begin with a valid directory', g:plug_home))
  544. endtry
  545. endif
  546. call s:prepare()
  547. call append(0, a:pull ? 'Updating plugins' : 'Installing plugins')
  548. call append(1, '['. s:lpad('', len(todo)) .']')
  549. normal! 2G
  550. redraw
  551. let s:prev_update = { 'errors': [], 'pull': a:pull, 'force': a:force, 'new': {}, 'threads': threads }
  552. if has('ruby') && threads > 1
  553. try
  554. let imd = &imd
  555. if s:mac_gui
  556. set noimd
  557. endif
  558. call s:update_parallel(a:pull, todo, threads)
  559. catch
  560. let lines = getline(4, '$')
  561. let printed = {}
  562. silent 4,$d
  563. for line in lines
  564. let name = get(matchlist(line, '^. \([^:]\+\):'), 1, '')
  565. if empty(name) || !has_key(printed, name)
  566. call append('$', line)
  567. if !empty(name)
  568. let printed[name] = 1
  569. if line[0] == 'x' && index(s:prev_update.errors, name) < 0
  570. call add(s:prev_update.errors, name)
  571. end
  572. endif
  573. endif
  574. endfor
  575. finally
  576. let &imd = imd
  577. endtry
  578. else
  579. call s:update_serial(a:pull, todo)
  580. endif
  581. call s:do(a:pull, a:force, filter(copy(todo), 'has_key(v:val, "do")'))
  582. call s:finish(a:pull)
  583. call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(st)))[0] . ' sec.')
  584. endfunction
  585. function! s:update_progress(pull, cnt, bar, total)
  586. call setline(1, (a:pull ? 'Updating' : 'Installing').
  587. \ ' plugins ('.a:cnt.'/'.a:total.')')
  588. call s:progress_bar(2, a:bar, a:total)
  589. normal! 2G
  590. redraw
  591. endfunction
  592. function! s:update_serial(pull, todo)
  593. let base = g:plug_home
  594. let todo = copy(a:todo)
  595. let total = len(todo)
  596. let done = {}
  597. let bar = ''
  598. for [name, spec] in items(todo)
  599. let done[name] = 1
  600. if isdirectory(spec.dir)
  601. execute 'cd '.s:esc(spec.dir)
  602. let [valid, msg] = s:git_valid(spec, 0, 0)
  603. if valid
  604. let result = a:pull ?
  605. \ s:system(
  606. \ printf('git checkout -q %s 2>&1 && git pull origin %s 2>&1 && git submodule update --init --recursive 2>&1',
  607. \ s:shellesc(spec.branch), s:shellesc(spec.branch))) : 'Already installed'
  608. let error = a:pull ? v:shell_error != 0 : 0
  609. else
  610. let result = msg
  611. let error = 1
  612. endif
  613. cd -
  614. else
  615. let result = s:system(
  616. \ printf('git clone --recursive %s -b %s %s 2>&1 && cd %s && git submodule update --init --recursive 2>&1',
  617. \ s:shellesc(spec.uri),
  618. \ s:shellesc(spec.branch),
  619. \ s:shellesc(s:trim(spec.dir)),
  620. \ s:shellesc(spec.dir)))
  621. let error = v:shell_error != 0
  622. if !error | let s:prev_update.new[name] = 1 | endif
  623. endif
  624. let bar .= error ? 'x' : '='
  625. if error
  626. call add(s:prev_update.errors, name)
  627. endif
  628. call append(3, s:format_message(!error, name, result))
  629. call s:update_progress(a:pull, len(done), bar, total)
  630. endfor
  631. endfunction
  632. function! s:update_parallel(pull, todo, threads)
  633. ruby << EOF
  634. module PlugStream
  635. SEP = ["\r", "\n", nil]
  636. def get_line
  637. buffer = ''
  638. loop do
  639. char = readchar rescue return
  640. if SEP.include? char.chr
  641. buffer << $/
  642. break
  643. else
  644. buffer << char
  645. end
  646. end
  647. buffer
  648. end
  649. end unless defined?(PlugStream)
  650. def esc arg
  651. %["#{arg.gsub('"', '\"')}"]
  652. end
  653. def killall pid
  654. pids = [pid]
  655. unless `which pgrep`.empty?
  656. children = pids
  657. until children.empty?
  658. children = children.map { |pid|
  659. `pgrep -P #{pid}`.lines.map { |l| l.chomp }
  660. }.flatten
  661. pids += children
  662. end
  663. end
  664. pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
  665. end
  666. require 'thread'
  667. require 'fileutils'
  668. require 'timeout'
  669. running = true
  670. iswin = VIM::evaluate('s:is_win').to_i == 1
  671. pull = VIM::evaluate('a:pull').to_i == 1
  672. base = VIM::evaluate('g:plug_home')
  673. all = VIM::evaluate('a:todo')
  674. limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
  675. tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1
  676. nthr = VIM::evaluate('a:threads').to_i
  677. maxy = VIM::evaluate('winheight(".")').to_i
  678. cd = iswin ? 'cd /d' : 'cd'
  679. tot = VIM::evaluate('len(a:todo)') || 0
  680. bar = ''
  681. skip = 'Already installed'
  682. mtx = Mutex.new
  683. take1 = proc { mtx.synchronize { running && all.shift } }
  684. logh = proc {
  685. cnt = bar.length
  686. $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
  687. $curbuf[2] = '[' + bar.ljust(tot) + ']'
  688. VIM::command('normal! 2G')
  689. VIM::command('redraw') unless iswin
  690. }
  691. where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } }
  692. log = proc { |name, result, type|
  693. mtx.synchronize do
  694. ing = ![true, false].include?(type)
  695. bar += type ? '=' : 'x' unless ing
  696. b = case type
  697. when :install then '+' when :update then '*'
  698. when true, nil then '-' else
  699. VIM::command("call add(s:prev_update.errors, '#{name}')")
  700. 'x'
  701. end
  702. result =
  703. if type || type.nil?
  704. ["#{b} #{name}: #{result.lines.to_a.last}"]
  705. elsif result =~ /^Interrupted|^Timeout/
  706. ["#{b} #{name}: #{result}"]
  707. else
  708. ["#{b} #{name}"] + result.lines.map { |l| " " << l }
  709. end
  710. if lnum = where.call(name)
  711. $curbuf.delete lnum
  712. lnum = 4 if ing && lnum > maxy
  713. end
  714. result.each_with_index do |line, offset|
  715. $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp)
  716. end
  717. logh.call
  718. end
  719. }
  720. bt = proc { |cmd, name, type|
  721. tried = timeout = 0
  722. begin
  723. tried += 1
  724. timeout += limit
  725. fd = nil
  726. data = ''
  727. if iswin
  728. Timeout::timeout(timeout) do
  729. tmp = VIM::evaluate('tempname()')
  730. system("#{cmd} > #{tmp}")
  731. data = File.read(tmp).chomp
  732. File.unlink tmp rescue nil
  733. end
  734. else
  735. fd = IO.popen(cmd).extend(PlugStream)
  736. first_line = true
  737. log_prob = 1.0 / nthr
  738. while line = Timeout::timeout(timeout) { fd.get_line }
  739. data << line
  740. log.call name, line.chomp, type if name && (first_line || rand < log_prob)
  741. first_line = false
  742. end
  743. fd.close
  744. end
  745. [$? == 0, data.chomp]
  746. rescue Timeout::Error, Interrupt => e
  747. if fd && !fd.closed?
  748. killall fd.pid
  749. fd.close
  750. end
  751. if e.is_a?(Timeout::Error) && tried < tries
  752. 3.downto(1) do |countdown|
  753. s = countdown > 1 ? 's' : ''
  754. log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type
  755. sleep 1
  756. end
  757. log.call name, 'Retrying ...', type
  758. retry
  759. end
  760. [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"]
  761. end
  762. }
  763. main = Thread.current
  764. threads = []
  765. watcher = Thread.new {
  766. while VIM::evaluate('getchar(1)')
  767. sleep 0.1
  768. end
  769. mtx.synchronize do
  770. running = false
  771. threads.each { |t| t.raise Interrupt }
  772. end
  773. threads.each { |t| t.join rescue nil }
  774. main.kill
  775. }
  776. refresh = Thread.new {
  777. while true
  778. mtx.synchronize do
  779. break unless running
  780. VIM::command('noautocmd normal! a')
  781. end
  782. sleep 0.2
  783. end
  784. } if VIM::evaluate('s:mac_gui') == 1
  785. progress = iswin ? '' : '--progress'
  786. [all.length, nthr].min.times do
  787. mtx.synchronize do
  788. threads << Thread.new {
  789. while pair = take1.call
  790. name = pair.first
  791. dir, uri, branch = pair.last.values_at *%w[dir uri branch]
  792. branch = esc branch
  793. subm = "git submodule update --init --recursive 2>&1"
  794. exists = File.directory? dir
  795. ok, result =
  796. if exists
  797. dir = esc dir
  798. ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url", nil, nil
  799. current_uri = data.lines.to_a.last
  800. if !ret
  801. if data =~ /^Interrupted|^Timeout/
  802. [false, data]
  803. else
  804. [false, [data.chomp, "PlugClean required."].join($/)]
  805. end
  806. elsif current_uri.sub(/git::?@/, '') != uri.sub(/git::?@/, '')
  807. [false, ["Invalid URI: #{current_uri}",
  808. "Expected: #{uri}",
  809. "PlugClean required."].join($/)]
  810. else
  811. if pull
  812. log.call name, 'Updating ...', :update
  813. bt.call "#{cd} #{dir} && git checkout -q #{branch} 2>&1 && (git pull origin #{branch} #{progress} 2>&1 && #{subm})", name, :update
  814. else
  815. [true, skip]
  816. end
  817. end
  818. else
  819. d = esc dir.sub(%r{[\\/]+$}, '')
  820. log.call name, 'Installing ...', :install
  821. bt.call "(git clone #{progress} --recursive #{uri} -b #{branch} #{d} 2>&1 && cd #{esc dir} && #{subm})", name, :install
  822. end
  823. mtx.synchronize { VIM::command("let s:prev_update.new['#{name}'] = 1") } if !exists && ok
  824. log.call name, result, ok
  825. end
  826. } if running
  827. end
  828. end
  829. threads.each { |t| t.join rescue nil }
  830. logh.call
  831. refresh.kill if refresh
  832. watcher.kill
  833. EOF
  834. endfunction
  835. function! s:shellesc(arg)
  836. return '"'.substitute(a:arg, '"', '\\"', 'g').'"'
  837. endfunction
  838. function! s:glob_dir(path)
  839. return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
  840. endfunction
  841. function! s:progress_bar(line, bar, total)
  842. call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']')
  843. endfunction
  844. function! s:compare_git_uri(a, b)
  845. let a = substitute(a:a, 'git:\{1,2}@', '', '')
  846. let b = substitute(a:b, 'git:\{1,2}@', '', '')
  847. return a ==# b
  848. endfunction
  849. function! s:format_message(ok, name, message)
  850. if a:ok
  851. return [printf('- %s: %s', a:name, s:lastline(a:message))]
  852. else
  853. let lines = map(split(a:message, '\n'), '" ".v:val')
  854. return extend([printf('x %s:', a:name)], lines)
  855. endif
  856. endfunction
  857. function! s:system(cmd)
  858. return system(s:is_win ? '('.a:cmd.')' : a:cmd)
  859. endfunction
  860. function! s:system_chomp(str)
  861. let ret = s:system(a:str)
  862. return v:shell_error ? '' : substitute(ret, '\n$', '', '')
  863. endfunction
  864. function! s:git_valid(spec, check_branch, cd)
  865. let ret = 1
  866. let msg = 'OK'
  867. if isdirectory(a:spec.dir)
  868. if a:cd | execute 'cd ' . s:esc(a:spec.dir) | endif
  869. let result = split(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url'), '\n')
  870. let remote = result[-1]
  871. if v:shell_error
  872. let msg = join([remote, 'PlugClean required.'], "\n")
  873. let ret = 0
  874. elseif !s:compare_git_uri(remote, a:spec.uri)
  875. let msg = join(['Invalid URI: '.remote,
  876. \ 'Expected: '.a:spec.uri,
  877. \ 'PlugClean required.'], "\n")
  878. let ret = 0
  879. elseif a:check_branch
  880. let branch = result[0]
  881. if a:spec.branch !=# branch
  882. let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1')
  883. if a:spec.branch !=# tag
  884. let msg = printf('Invalid branch/tag: %s (expected: %s). Try PlugUpdate.',
  885. \ (empty(tag) ? branch : tag), a:spec.branch)
  886. let ret = 0
  887. endif
  888. endif
  889. endif
  890. if a:cd | cd - | endif
  891. else
  892. let msg = 'Not found'
  893. let ret = 0
  894. endif
  895. return [ret, msg]
  896. endfunction
  897. function! s:clean(force)
  898. call s:prepare()
  899. call append(0, 'Searching for unused plugins in '.g:plug_home)
  900. call append(1, '')
  901. " List of valid directories
  902. let dirs = []
  903. let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
  904. let [cnt, total] = [0, len(managed)]
  905. for spec in values(managed)
  906. if s:git_valid(spec, 0, 1)[0]
  907. call add(dirs, spec.dir)
  908. endif
  909. let cnt += 1
  910. call s:progress_bar(2, repeat('=', cnt), total)
  911. normal! 2G
  912. redraw
  913. endfor
  914. let allowed = {}
  915. for dir in dirs
  916. let allowed[dir] = 1
  917. for child in s:glob_dir(dir)
  918. let allowed[child] = 1
  919. endfor
  920. endfor
  921. let todo = []
  922. let found = sort(s:glob_dir(g:plug_home))
  923. while !empty(found)
  924. let f = remove(found, 0)
  925. if !has_key(allowed, f) && isdirectory(f)
  926. call add(todo, f)
  927. call append(line('$'), '- ' . f)
  928. let found = filter(found, 'stridx(v:val, f) != 0')
  929. end
  930. endwhile
  931. normal! G
  932. redraw
  933. if empty(todo)
  934. call append(line('$'), 'Already clean.')
  935. else
  936. call inputsave()
  937. let yes = a:force || (input('Proceed? (Y/N) ') =~? '^y')
  938. call inputrestore()
  939. if yes
  940. for dir in todo
  941. if isdirectory(dir)
  942. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(dir))
  943. endif
  944. endfor
  945. call append(line('$'), 'Removed.')
  946. else
  947. call append(line('$'), 'Cancelled.')
  948. endif
  949. endif
  950. normal! G
  951. endfunction
  952. function! s:upgrade()
  953. if executable('curl')
  954. let mee = s:shellesc(s:me)
  955. let new = s:shellesc(s:me . '.new')
  956. echo 'Downloading '. s:plug_source
  957. redraw
  958. let mv = s:is_win ? 'move /Y' : 'mv -f'
  959. let cp = s:is_win ? 'copy /Y' : 'cp -f'
  960. call system(printf(
  961. \ 'curl -fLo %s %s && '.cp.' %s %s.old && '.mv.' %s %s',
  962. \ new, s:plug_source, mee, mee, new, mee))
  963. if v:shell_error
  964. return s:err('Error upgrading vim-plug')
  965. endif
  966. elseif has('ruby')
  967. echo 'Downloading '. s:plug_source
  968. try
  969. ruby << EOF
  970. require 'open-uri'
  971. require 'fileutils'
  972. me = VIM::evaluate('s:me')
  973. old = me + '.old'
  974. new = me + '.new'
  975. File.open(new, 'w') do |f|
  976. f << open(VIM::evaluate('s:plug_source')).read
  977. end
  978. FileUtils.cp me, old
  979. File.rename new, me
  980. EOF
  981. catch
  982. return s:err('Error upgrading vim-plug')
  983. endtry
  984. else
  985. return s:err('curl executable or ruby support not found')
  986. endif
  987. unlet g:loaded_plug
  988. echo 'Downloaded '. s:plug_source
  989. return 1
  990. endfunction
  991. function! s:upgrade_specs()
  992. for spec in values(g:plugs)
  993. let spec.frozen = get(spec, 'frozen', 0)
  994. endfor
  995. endfunction
  996. function! s:status()
  997. call s:prepare()
  998. call append(0, 'Checking plugins')
  999. call append(1, '')
  1000. let ecnt = 0
  1001. let unloaded = 0
  1002. let [cnt, total] = [0, len(g:plugs)]
  1003. for [name, spec] in items(g:plugs)
  1004. if has_key(spec, 'uri')
  1005. if isdirectory(spec.dir)
  1006. let [valid, msg] = s:git_valid(spec, 1, 1)
  1007. else
  1008. let [valid, msg] = [0, 'Not found. Try PlugInstall.']
  1009. endif
  1010. else
  1011. if isdirectory(spec.dir)
  1012. let [valid, msg] = [1, 'OK']
  1013. else
  1014. let [valid, msg] = [0, 'Not found.']
  1015. endif
  1016. endif
  1017. let cnt += 1
  1018. let ecnt += !valid
  1019. " `s:loaded` entry can be missing if PlugUpgraded
  1020. if valid && get(s:loaded, spec.dir, -1) == 0
  1021. let unloaded = 1
  1022. let msg .= ' (not loaded)'
  1023. endif
  1024. call s:progress_bar(2, repeat('=', cnt), total)
  1025. call append(3, s:format_message(valid, name, msg))
  1026. normal! 2G
  1027. redraw
  1028. endfor
  1029. call setline(1, 'Finished. '.ecnt.' error(s).')
  1030. normal! gg
  1031. setlocal nomodifiable
  1032. if unloaded
  1033. echo "Press 'L' on each line to load plugin"
  1034. nnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr>
  1035. xnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr>
  1036. end
  1037. endfunction
  1038. function! s:status_load(lnum)
  1039. let line = getline(a:lnum)
  1040. let matches = matchlist(line, '^- \([^:]*\):.*(not loaded)$')
  1041. if !empty(matches)
  1042. let name = matches[1]
  1043. call plug#load(name)
  1044. setlocal modifiable
  1045. call setline(a:lnum, substitute(line, ' (not loaded)$', '', ''))
  1046. setlocal nomodifiable
  1047. endif
  1048. endfunction
  1049. function! s:is_preview_window_open()
  1050. silent! wincmd P
  1051. if &previewwindow
  1052. wincmd p
  1053. return 1
  1054. endif
  1055. return 0
  1056. endfunction
  1057. function! s:find_name(lnum)
  1058. for lnum in reverse(range(1, a:lnum))
  1059. let line = getline(lnum)
  1060. if empty(line)
  1061. return ''
  1062. endif
  1063. let name = matchstr(line, '\(^- \)\@<=[^:]\+')
  1064. if !empty(name)
  1065. return name
  1066. endif
  1067. endfor
  1068. return ''
  1069. endfunction
  1070. function! s:preview_commit()
  1071. if b:plug_preview < 0
  1072. let b:plug_preview = !s:is_preview_window_open()
  1073. endif
  1074. let sha = matchstr(getline('.'), '\(^ \)\@<=[0-9a-z]\{7}')
  1075. if empty(sha)
  1076. return
  1077. endif
  1078. let name = s:find_name(line('.'))
  1079. if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir)
  1080. return
  1081. endif
  1082. execute 'cd '.s:esc(g:plugs[name].dir)
  1083. execute 'pedit '.sha
  1084. wincmd P
  1085. setlocal filetype=git buftype=nofile nobuflisted
  1086. execute 'silent read !git show '.sha
  1087. normal! ggdd
  1088. wincmd p
  1089. cd -
  1090. endfunction
  1091. function! s:section(flags)
  1092. call search('\(^- \)\@<=.', a:flags)
  1093. endfunction
  1094. function! s:diff()
  1095. call s:prepare()
  1096. call append(0, 'Collecting updated changes ...')
  1097. normal! gg
  1098. redraw
  1099. let cnt = 0
  1100. for [k, v] in items(g:plugs)
  1101. if !isdirectory(v.dir) || !s:is_managed(k)
  1102. continue
  1103. endif
  1104. execute 'cd '.s:esc(v.dir)
  1105. let diff = system('git log --pretty=format:"%h %s (%cr)" "HEAD...HEAD@{1}"')
  1106. if !v:shell_error && !empty(diff)
  1107. call append(1, '')
  1108. call append(2, '- '.k.':')
  1109. call append(3, map(split(diff, '\n'), '" ". v:val'))
  1110. let cnt += 1
  1111. normal! gg
  1112. redraw
  1113. endif
  1114. cd -
  1115. endfor
  1116. call setline(1, cnt == 0 ? 'No updates.' : 'Last update:')
  1117. nnoremap <silent> <buffer> <cr> :silent! call <SID>preview_commit()<cr>
  1118. nnoremap <silent> <buffer> X :call <SID>revert()<cr>
  1119. normal! gg
  1120. setlocal nomodifiable
  1121. if cnt > 0
  1122. echo "Press 'X' on each block to revert the update"
  1123. endif
  1124. endfunction
  1125. function! s:revert()
  1126. let name = s:find_name(line('.'))
  1127. if empty(name) || !has_key(g:plugs, name) ||
  1128. \ input(printf('Revert the update of %s? (Y/N) ', name)) !~? '^y'
  1129. return
  1130. endif
  1131. execute 'cd '.s:esc(g:plugs[name].dir)
  1132. call system('git reset --hard HEAD@{1} && git checkout '.s:esc(g:plugs[name].branch))
  1133. cd -
  1134. setlocal modifiable
  1135. normal! dap
  1136. setlocal nomodifiable
  1137. echo 'Reverted.'
  1138. endfunction
  1139. let s:first_rtp = s:esc(get(split(&rtp, ','), 0, ''))
  1140. let s:last_rtp = s:esc(get(split(&rtp, ','), -1, ''))
  1141. if exists('g:plugs')
  1142. let g:plugs_order = get(g:, 'plugs_order', keys(g:plugs))
  1143. call s:upgrade_specs()
  1144. call s:define_commands()
  1145. endif
  1146. let &cpo = s:cpo_save
  1147. unlet s:cpo_save