comparison vim/autoload/plug.vim @ 247:5199030e3e2c

Update vimplug
author zegervdv <zegervdv@me.com>
date Sun, 22 Feb 2015 22:38:06 +0100
parents 0994a5f99432
children f0d3b37101c0
comparison
equal deleted inserted replaced
246:624d06dcf54c 247:5199030e3e2c
71 let s:plug_src = 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim' 71 let s:plug_src = 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'
72 let s:plug_tab = get(s:, 'plug_tab', -1) 72 let s:plug_tab = get(s:, 'plug_tab', -1)
73 let s:plug_buf = get(s:, 'plug_buf', -1) 73 let s:plug_buf = get(s:, 'plug_buf', -1)
74 let s:mac_gui = has('gui_macvim') && has('gui_running') 74 let s:mac_gui = has('gui_macvim') && has('gui_running')
75 let s:is_win = has('win32') || has('win64') 75 let s:is_win = has('win32') || has('win64')
76 let s:py2 = has('python') && !s:is_win
76 let s:ruby = has('ruby') && (v:version >= 703 || v:version == 702 && has('patch374')) 77 let s:ruby = has('ruby') && (v:version >= 703 || v:version == 702 && has('patch374'))
77 let s:nvim = has('nvim') && !s:is_win 78 let s:nvim = has('nvim') && !s:is_win
78 let s:me = resolve(expand('<sfile>:p')) 79 let s:me = resolve(expand('<sfile>:p'))
79 let s:base_spec = { 'branch': 'master', 'frozen': 0 } 80 let s:base_spec = { 'branch': 'master', 'frozen': 0 }
80 let s:TYPE = { 81 let s:TYPE = {
233 234
234 function! s:trim(str) 235 function! s:trim(str)
235 return substitute(a:str, '[\/]\+$', '', '') 236 return substitute(a:str, '[\/]\+$', '', '')
236 endfunction 237 endfunction
237 238
239 function! s:version_requirement(val, min)
240 for idx in range(0, len(a:min) - 1)
241 let v = get(a:val, idx, 0)
242 if v < a:min[idx] | return 0
243 elseif v > a:min[idx] | return 1
244 endif
245 endfor
246 return 1
247 endfunction
248
238 function! s:git_version_requirement(...) 249 function! s:git_version_requirement(...)
239 let s:git_version = get(s:, 'git_version', 250 let s:git_version = get(s:, 'git_version',
240 \ map(split(split(s:system('git --version'))[-1], '\.'), 'str2nr(v:val)')) 251 \ map(split(split(s:system('git --version'))[-1], '\.'), 'str2nr(v:val)'))
241 for idx in range(0, a:0 - 1) 252 return s:version_requirement(s:git_version, a:000)
242 let v = get(s:git_version, idx, 0)
243 if v < a:000[idx] | return 0
244 elseif v > a:000[idx] | return 1
245 endif
246 endfor
247 return 1
248 endfunction 253 endfunction
249 254
250 function! s:progress_opt(base) 255 function! s:progress_opt(base)
251 return a:base && !s:is_win && 256 return a:base && !s:is_win &&
252 \ s:git_version_requirement(1, 7, 1) ? '--progress' : '' 257 \ s:git_version_requirement(1, 7, 1) ? '--progress' : ''
432 437
433 function! s:parse_options(arg) 438 function! s:parse_options(arg)
434 let opts = copy(s:base_spec) 439 let opts = copy(s:base_spec)
435 let type = type(a:arg) 440 let type = type(a:arg)
436 if type == s:TYPE.string 441 if type == s:TYPE.string
437 let opts.branch = a:arg 442 let opts.tag = a:arg
438 elseif type == s:TYPE.dict 443 elseif type == s:TYPE.dict
439 call extend(opts, a:arg) 444 call extend(opts, a:arg)
440 if has_key(opts, 'tag')
441 let opts.branch = remove(opts, 'tag')
442 endif
443 if has_key(opts, 'dir') 445 if has_key(opts, 'dir')
444 let opts.dir = s:dirpath(expand(opts.dir)) 446 let opts.dir = s:dirpath(expand(opts.dir))
445 endif 447 endif
446 else 448 else
447 throw 'Invalid argument type (expected: string or dictionary)' 449 throw 'Invalid argument type (expected: string or dictionary)'
604 let s:plug_buf = winbufnr(0) 606 let s:plug_buf = winbufnr(0)
605 call s:assign_name() 607 call s:assign_name()
606 endif 608 endif
607 silent! unmap <buffer> <cr> 609 silent! unmap <buffer> <cr>
608 silent! unmap <buffer> L 610 silent! unmap <buffer> L
611 silent! unmap <buffer> o
609 silent! unmap <buffer> X 612 silent! unmap <buffer> X
610 setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline modifiable 613 setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline modifiable
611 setf vim-plug 614 setf vim-plug
612 call s:syntax() 615 call s:syntax()
613 endfunction 616 endfunction
742 \ 'todo': copy(todo), 745 \ 'todo': copy(todo),
743 \ 'errors': [], 746 \ 'errors': [],
744 \ 'pull': a:pull, 747 \ 'pull': a:pull,
745 \ 'force': a:force, 748 \ 'force': a:force,
746 \ 'new': {}, 749 \ 'new': {},
747 \ 'threads': (s:ruby || s:nvim) ? min([len(todo), threads]) : 1, 750 \ 'threads': (s:py2 || s:ruby || s:nvim) ? min([len(todo), threads]) : 1,
748 \ 'bar': '', 751 \ 'bar': '',
749 \ 'fin': 0 752 \ 'fin': 0
750 \ } 753 \ }
751 754
752 call s:prepare() 755 call s:prepare()
753 call append(0, ['', '']) 756 call append(0, ['', ''])
754 normal! 2G 757 normal! 2G
755 758
756 if s:ruby && s:update.threads > 1 759 " Python version requirement (>= 2.7)
760 if s:py2 && !s:ruby && !s:nvim && s:update.threads > 1
761 redir => pyv
762 silent python import platform; print(platform.python_version())
763 redir END
764 let s:py2 = s:version_requirement(
765 \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 7])
766 endif
767 if (s:py2 || s:ruby) && !s:nvim && s:update.threads > 1
757 try 768 try
758 let imd = &imd 769 let imd = &imd
759 if s:mac_gui 770 if s:mac_gui
760 set noimd 771 set noimd
761 endif 772 endif
762 call s:update_ruby() 773 if s:ruby
774 call s:update_ruby()
775 else
776 call s:update_python()
777 endif
763 catch 778 catch
764 let lines = getline(4, '$') 779 let lines = getline(4, '$')
765 let printed = {} 780 let printed = {}
766 silent! 4,$d _ 781 silent! 4,$d _
767 for line in lines 782 for line in lines
947 let new = !isdirectory(spec.dir) 962 let new = !isdirectory(spec.dir)
948 963
949 call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') 964 call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...')
950 redraw 965 redraw
951 966
967 let checkout = s:shellesc(has_key(spec, 'tag') ? spec.tag : spec.branch)
968 let merge = s:shellesc(has_key(spec, 'tag') ? spec.tag : 'origin/'.spec.branch)
969
952 if !new 970 if !new
953 let [valid, msg] = s:git_valid(spec, 0) 971 let [valid, msg] = s:git_valid(spec, 0)
954 if valid 972 if valid
955 if pull 973 if pull
956 call s:spawn(name, 974 call s:spawn(name,
957 \ printf('(git fetch %s 2>&1 && git checkout -q %s 2>&1 && git merge --ff-only origin/%s 2>&1 && git submodule update --init --recursive 2>&1)', 975 \ printf('(git fetch %s 2>&1 && git checkout -q %s 2>&1 && git merge --ff-only %s 2>&1 && git submodule update --init --recursive 2>&1)',
958 \ prog, s:shellesc(spec.branch), s:shellesc(spec.branch)), { 'dir': spec.dir }) 976 \ prog, checkout, merge), { 'dir': spec.dir })
959 else 977 else
960 let s:jobs[name] = { 'running': 0, 'result': 'Already installed', 'error': 0 } 978 let s:jobs[name] = { 'running': 0, 'result': 'Already installed', 'error': 0 }
961 endif 979 endif
962 else 980 else
963 let s:jobs[name] = { 'running': 0, 'result': msg, 'error': 1 } 981 let s:jobs[name] = { 'running': 0, 'result': msg, 'error': 1 }
965 else 983 else
966 call s:spawn(name, 984 call s:spawn(name,
967 \ printf('git clone %s --recursive %s -b %s %s 2>&1', 985 \ printf('git clone %s --recursive %s -b %s %s 2>&1',
968 \ prog, 986 \ prog,
969 \ s:shellesc(spec.uri), 987 \ s:shellesc(spec.uri),
970 \ s:shellesc(spec.branch), 988 \ checkout,
971 \ s:shellesc(s:trim(spec.dir))), { 'new': 1 }) 989 \ s:shellesc(s:trim(spec.dir))), { 'new': 1 })
972 endif 990 endif
973 991
974 if !s:jobs[name].running 992 if !s:jobs[name].running
975 call s:reap(name) 993 call s:reap(name)
976 endif 994 endif
977 if len(s:jobs) >= s:update.threads 995 if len(s:jobs) >= s:update.threads
978 break 996 break
979 endif 997 endif
980 endwhile 998 endwhile
999 endfunction
1000
1001 function! s:update_python()
1002 python << EOF
1003 """ Due to use of signals this function is POSIX only. """
1004 import datetime
1005 import functools
1006 import os
1007 import Queue
1008 import random
1009 import re
1010 import shutil
1011 import signal
1012 import subprocess
1013 import tempfile
1014 import threading as thr
1015 import time
1016 import traceback
1017 import vim
1018
1019 G_PULL = vim.eval('s:update.pull') == '1'
1020 G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1
1021 G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)'))
1022 G_PROGRESS = vim.eval('s:progress_opt(1)')
1023 G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads'))
1024 G_STOP = thr.Event()
1025
1026 class CmdTimedOut(Exception):
1027 pass
1028 class CmdFailed(Exception):
1029 pass
1030 class InvalidURI(Exception):
1031 pass
1032 class Action(object):
1033 INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-']
1034
1035 class GLog(object):
1036 ON = None
1037 LOGDIR = None
1038 @classmethod
1039 def write(cls, msg):
1040 if cls.ON is None:
1041 cls.ON = int(vim.eval('get(g:, "plug_log_on", 0)'))
1042 cls.LOGDIR = os.path.expanduser(vim.eval('get(g:, "plug_logs", "~/plug_logs")'))
1043 if cls.ON:
1044 if not os.path.exists(cls.LOGDIR):
1045 os.makedirs(cls.LOGDIR)
1046 cls._write(msg)
1047 @classmethod
1048 def _write(cls, msg):
1049 name = thr.current_thread().name
1050 fname = cls.LOGDIR + os.path.sep + name
1051 with open(fname, 'ab') as flog:
1052 ltime = datetime.datetime.now().strftime("%H:%M:%S.%f")
1053 msg = '[{},{}] {}{}'.format(name, ltime, msg, '\n')
1054 flog.write(msg)
1055
1056 class Buffer(object):
1057 def __init__(self, lock, num_plugs):
1058 self.bar = ''
1059 self.event = 'Updating' if vim.eval('s:update.pull') == '1' else 'Installing'
1060 self.is_win = vim.eval('s:is_win') == '1'
1061 self.lock = lock
1062 self.maxy = int(vim.eval('winheight(".")'))
1063 self.num_plugs = num_plugs
1064
1065 def _where(self, name):
1066 """ Find first line with name in current buffer. Return line num. """
1067 found, lnum = False, 0
1068 matcher = re.compile('^[-+x*] {}:'.format(name))
1069 for line in vim.current.buffer:
1070 if matcher.search(line) is not None:
1071 found = True
1072 break
1073 lnum += 1
1074
1075 if not found:
1076 lnum = -1
1077 return lnum
1078
1079 def header(self):
1080 curbuf = vim.current.buffer
1081 curbuf[0] = self.event + ' plugins ({}/{})'.format(len(self.bar), self.num_plugs)
1082
1083 num_spaces = self.num_plugs - len(self.bar)
1084 curbuf[1] = '[{}{}]'.format(self.bar, num_spaces * ' ')
1085
1086 vim.command('normal! 2G')
1087 if not self.is_win:
1088 vim.command('redraw')
1089
1090 def write(self, *args, **kwargs):
1091 with self.lock:
1092 self._write(*args, **kwargs)
1093
1094 def _write(self, action, name, lines):
1095 first, rest = lines[0], lines[1:]
1096 msg = ['{} {}{}{}'.format(action, name, ': ' if first else '', first)]
1097 padded_rest = [' ' + line for line in rest]
1098 msg.extend(padded_rest)
1099
1100 try:
1101 if action == Action.ERROR:
1102 self.bar += 'x'
1103 vim.command("call add(s:update.errors, '{}')".format(name))
1104 elif action == Action.DONE:
1105 self.bar += '='
1106
1107 curbuf = vim.current.buffer
1108 lnum = self._where(name)
1109 if lnum != -1: # Found matching line num
1110 del curbuf[lnum]
1111 if lnum > self.maxy and action in {Action.INSTALL, Action.UPDATE}:
1112 lnum = 3
1113 else:
1114 lnum = 3
1115 curbuf.append(msg, lnum)
1116
1117 self.header()
1118 except vim.error:
1119 GLog.write('Buffer Update FAILED.')
1120
1121 class Command(object):
1122 def __init__(self, cmd, cmd_dir=None, timeout=60, ntries=3, cb=None, clean=None):
1123 self.cmd = cmd
1124 self.cmd_dir = cmd_dir
1125 self.timeout = timeout
1126 self.ntries = ntries
1127 self.callback = cb if cb else (lambda msg: None)
1128 self.clean = clean
1129
1130 def attempt_cmd(self):
1131 """ Tries to run the command, returns result if no exceptions. """
1132 attempt = 0
1133 finished = False
1134 limit = self.timeout
1135
1136 while not finished:
1137 try:
1138 attempt += 1
1139 result = self.timeout_cmd()
1140 finished = True
1141 except CmdTimedOut:
1142 if attempt != self.ntries:
1143 for count in range(3, 0, -1):
1144 if G_STOP.is_set():
1145 raise KeyboardInterrupt
1146 msg = 'Timeout. Will retry in {} second{} ...'.format(
1147 count, 's' if count != 1 else '')
1148 self.callback([msg])
1149 time.sleep(1)
1150 self.timeout += limit
1151 self.callback(['Retrying ...'])
1152 else:
1153 raise
1154
1155 return result
1156
1157 def timeout_cmd(self):
1158 """ Execute a cmd & poll for callback. Returns list of output.
1159 Raises CmdFailed -> return code for Popen isn't 0
1160 Raises CmdTimedOut -> command exceeded timeout without new output
1161 """
1162 proc = None
1163 first_line = True
1164 try:
1165 tfile = tempfile.NamedTemporaryFile()
1166 proc = subprocess.Popen(self.cmd, cwd=self.cmd_dir, stdout=tfile,
1167 stderr=subprocess.STDOUT, shell=True, preexec_fn=os.setsid)
1168 while proc.poll() is None:
1169 # Yield this thread
1170 time.sleep(0.2)
1171
1172 if G_STOP.is_set():
1173 raise KeyboardInterrupt
1174
1175 if first_line or random.random() < G_LOG_PROB:
1176 first_line = False
1177 line = nonblock_read(tfile.name)
1178 if line:
1179 self.callback([line])
1180
1181 time_diff = time.time() - os.path.getmtime(tfile.name)
1182 if time_diff > self.timeout:
1183 raise CmdTimedOut(['Timeout!'])
1184
1185 tfile.seek(0)
1186 result = [line.rstrip() for line in tfile]
1187
1188 if proc.returncode != 0:
1189 msg = ['']
1190 msg.extend(result)
1191 raise CmdFailed(msg)
1192 except:
1193 if proc and proc.poll() is None:
1194 os.killpg(proc.pid, signal.SIGTERM)
1195 if self.clean:
1196 self.clean()
1197 raise
1198
1199 return result
1200
1201 class Plugin(object):
1202 def __init__(self, name, args, buf, lock):
1203 self.name = name
1204 self.args = args
1205 self.buf = buf
1206 self.lock = lock
1207 tag = args.get('tag', 0)
1208 self.checkout = esc(tag if tag else args['branch'])
1209 self.merge = esc(tag if tag else 'origin/' + args['branch'])
1210
1211 def manage(self):
1212 try:
1213 if os.path.exists(self.args['dir']):
1214 self.update()
1215 else:
1216 self.install()
1217 with self.lock:
1218 vim.command("let s:update.new['{}'] = 1".format(self.name))
1219 except (CmdTimedOut, CmdFailed, InvalidURI) as exc:
1220 self.write(Action.ERROR, self.name, exc.message)
1221 except KeyboardInterrupt:
1222 G_STOP.set()
1223 self.write(Action.ERROR, self.name, ['Interrupted!'])
1224 except:
1225 # Any exception except those above print stack trace
1226 msg = 'Trace:\n{}'.format(traceback.format_exc().rstrip())
1227 self.write(Action.ERROR, self.name, msg.split('\n'))
1228 raise
1229
1230 def install(self):
1231 target = self.args['dir']
1232
1233 def clean(target):
1234 def _clean():
1235 try:
1236 shutil.rmtree(target)
1237 except OSError:
1238 pass
1239 return _clean
1240
1241 self.write(Action.INSTALL, self.name, ['Installing ...'])
1242 callback = functools.partial(self.buf.write, Action.INSTALL, self.name)
1243 cmd = 'git clone {} --recursive {} -b {} {} 2>&1'.format(
1244 G_PROGRESS, self.args['uri'], self.checkout, esc(target))
1245 com = Command(cmd, None, G_TIMEOUT, G_RETRIES, callback, clean(target))
1246 result = com.attempt_cmd()
1247 self.write(Action.DONE, self.name, result[-1:])
1248
1249 def update(self):
1250 match = re.compile(r'git::?@')
1251 actual_uri = re.sub(match, '', self.repo_uri())
1252 expect_uri = re.sub(match, '', self.args['uri'])
1253 if actual_uri != expect_uri:
1254 msg = ['',
1255 'Invalid URI: {}'.format(actual_uri),
1256 'Expected {}'.format(expect_uri),
1257 'PlugClean required.']
1258 raise InvalidURI(msg)
1259
1260 if G_PULL:
1261 self.write(Action.UPDATE, self.name, ['Updating ...'])
1262 callback = functools.partial(self.buf.write, Action.UPDATE, self.name)
1263 cmds = ['git fetch {}'.format(G_PROGRESS),
1264 'git checkout -q {}'.format(self.checkout),
1265 'git merge --ff-only {}'.format(self.merge),
1266 'git submodule update --init --recursive']
1267 cmd = ' 2>&1 && '.join(cmds)
1268 GLog.write(cmd)
1269 com = Command(cmd, self.args['dir'], G_TIMEOUT, G_RETRIES, callback)
1270 result = com.attempt_cmd()
1271 GLog.write(result)
1272 self.write(Action.DONE, self.name, result[-1:])
1273 else:
1274 self.write(Action.DONE, self.name, ['Already installed'])
1275
1276 def repo_uri(self):
1277 cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url'
1278 command = Command(cmd, self.args['dir'], G_TIMEOUT, G_RETRIES)
1279 result = command.attempt_cmd()
1280 return result[-1]
1281
1282 def write(self, action, name, msg):
1283 GLog.write('{} {}: {}'.format(action, name, '\n'.join(msg)))
1284 self.buf.write(action, name, msg)
1285
1286 class PlugThread(thr.Thread):
1287 def __init__(self, tname, args):
1288 super(PlugThread, self).__init__()
1289 self.tname = tname
1290 self.args = args
1291
1292 def run(self):
1293 thr.current_thread().name = self.tname
1294 work_q, lock, buf = self.args
1295
1296 try:
1297 while not G_STOP.is_set():
1298 name, args = work_q.get_nowait()
1299 GLog.write('{}: Dir {}'.format(name, args['dir']))
1300 plug = Plugin(name, args, buf, lock)
1301 plug.manage()
1302 work_q.task_done()
1303 except Queue.Empty:
1304 GLog.write('Queue now empty.')
1305
1306 class RefreshThread(thr.Thread):
1307 def __init__(self, lock):
1308 super(RefreshThread, self).__init__()
1309 self.lock = lock
1310 self.running = True
1311
1312 def run(self):
1313 while self.running:
1314 with self.lock:
1315 vim.command('noautocmd normal! a')
1316 time.sleep(0.2)
1317
1318 def stop(self):
1319 self.running = False
1320
1321 def esc(name):
1322 return '"' + name.replace('"', '\"') + '"'
1323
1324 def nonblock_read(fname):
1325 """ Read a file with nonblock flag. Return the last line. """
1326 fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK)
1327 buf = os.read(fread, 100000)
1328 os.close(fread)
1329
1330 line = buf.rstrip('\r\n')
1331 left = max(line.rfind('\r'), line.rfind('\n'))
1332 if left != -1:
1333 left += 1
1334 line = line[left:]
1335
1336 return line
1337
1338 def main():
1339 thr.current_thread().name = 'main'
1340 GLog.write('')
1341 if GLog.ON and os.path.exists(GLog.LOGDIR):
1342 shutil.rmtree(GLog.LOGDIR)
1343
1344 threads = []
1345 nthreads = int(vim.eval('s:update.threads'))
1346 plugs = vim.eval('s:update.todo')
1347 mac_gui = vim.eval('s:mac_gui') == '1'
1348 is_win = vim.eval('s:is_win') == '1'
1349 GLog.write('Plugs: {}'.format(plugs))
1350 GLog.write('PULL: {}, WIN: {}, MAC: {}'.format(G_PULL, is_win, mac_gui))
1351 GLog.write('Num Threads: {}'.format(nthreads))
1352
1353 lock = thr.Lock()
1354 buf = Buffer(lock, len(plugs))
1355 work_q = Queue.Queue()
1356 for work in plugs.items():
1357 work_q.put(work)
1358
1359 GLog.write('Starting Threads')
1360 for num in range(nthreads):
1361 tname = 'PlugT-{0:02}'.format(num)
1362 thread = PlugThread(tname, (work_q, lock, buf))
1363 thread.start()
1364 threads.append(thread)
1365 if mac_gui:
1366 rthread = RefreshThread(lock)
1367 rthread.start()
1368
1369 GLog.write('Joining Live Threads')
1370 for thread in threads:
1371 thread.join()
1372 if mac_gui:
1373 rthread.stop()
1374 rthread.join()
1375 GLog.write('Cleanly Exited Main')
1376
1377 main()
1378 EOF
981 endfunction 1379 endfunction
982 1380
983 function! s:update_ruby() 1381 function! s:update_ruby()
984 ruby << EOF 1382 ruby << EOF
985 module PlugStream 1383 module PlugStream
1079 fd = nil 1477 fd = nil
1080 data = '' 1478 data = ''
1081 if iswin 1479 if iswin
1082 Timeout::timeout(timeout) do 1480 Timeout::timeout(timeout) do
1083 tmp = VIM::evaluate('tempname()') 1481 tmp = VIM::evaluate('tempname()')
1084 system("#{cmd} > #{tmp}") 1482 system("(#{cmd}) > #{tmp}")
1085 data = File.read(tmp).chomp 1483 data = File.read(tmp).chomp
1086 File.unlink tmp rescue nil 1484 File.unlink tmp rescue nil
1087 end 1485 end
1088 else 1486 else
1089 fd = IO.popen(cmd).extend(PlugStream) 1487 fd = IO.popen(cmd).extend(PlugStream)
1142 nthr.times do 1540 nthr.times do
1143 mtx.synchronize do 1541 mtx.synchronize do
1144 threads << Thread.new { 1542 threads << Thread.new {
1145 while pair = take1.call 1543 while pair = take1.call
1146 name = pair.first 1544 name = pair.first
1147 dir, uri, branch = pair.last.values_at *%w[dir uri branch] 1545 dir, uri, branch, tag = pair.last.values_at *%w[dir uri branch tag]
1148 branch = esc branch 1546 checkout = esc(tag ? tag : branch)
1547 merge = esc(tag ? tag : "origin/#{branch}")
1149 subm = "git submodule update --init --recursive 2>&1" 1548 subm = "git submodule update --init --recursive 2>&1"
1150 exists = File.directory? dir 1549 exists = File.directory? dir
1151 ok, result = 1550 ok, result =
1152 if exists 1551 if exists
1153 dir = esc dir 1552 dir = iswin ? dir : esc(dir)
1154 ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url", nil, nil, nil 1553 ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url", nil, nil, nil
1155 current_uri = data.lines.to_a.last 1554 current_uri = data.lines.to_a.last
1156 if !ret 1555 if !ret
1157 if data =~ /^Interrupted|^Timeout/ 1556 if data =~ /^Interrupted|^Timeout/
1158 [false, data] 1557 [false, data]
1164 "Expected: #{uri}", 1563 "Expected: #{uri}",
1165 "PlugClean required."].join($/)] 1564 "PlugClean required."].join($/)]
1166 else 1565 else
1167 if pull 1566 if pull
1168 log.call name, 'Updating ...', :update 1567 log.call name, 'Updating ...', :update
1169 bt.call "#{cd} #{dir} && (git fetch #{progress} 2>&1 && git checkout -q #{branch} 2>&1 && git merge --ff-only origin/#{branch} 2>&1 && #{subm})", name, :update, nil 1568 bt.call "#{cd} #{dir} && git fetch #{progress} 2>&1 && git checkout -q #{checkout} 2>&1 && git merge --ff-only #{merge} 2>&1 && #{subm}", name, :update, nil
1170 else 1569 else
1171 [true, skip] 1570 [true, skip]
1172 end 1571 end
1173 end 1572 end
1174 else 1573 else
1175 d = esc dir.sub(%r{[\\/]+$}, '') 1574 d = esc dir.sub(%r{[\\/]+$}, '')
1176 log.call name, 'Installing ...', :install 1575 log.call name, 'Installing ...', :install
1177 bt.call "git clone #{progress} --recursive #{uri} -b #{branch} #{d} 2>&1", name, :install, proc { 1576 bt.call "git clone #{progress} --recursive #{uri} -b #{checkout} #{d} 2>&1", name, :install, proc {
1178 FileUtils.rm_rf dir 1577 FileUtils.rm_rf dir
1179 } 1578 }
1180 end 1579 end
1181 mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok 1580 mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok
1182 log.call name, result, ok 1581 log.call name, result, ok
1190 watcher.kill 1589 watcher.kill
1191 EOF 1590 EOF
1192 endfunction 1591 endfunction
1193 1592
1194 function! s:shellesc(arg) 1593 function! s:shellesc(arg)
1195 return '"'.substitute(a:arg, '"', '\\"', 'g').'"' 1594 return '"'.escape(a:arg, '"').'"'
1196 endfunction 1595 endfunction
1197 1596
1198 function! s:glob_dir(path) 1597 function! s:glob_dir(path)
1199 return map(filter(s:lines(globpath(a:path, '**')), 'isdirectory(v:val)'), 's:dirpath(v:val)') 1598 return map(filter(s:lines(globpath(a:path, '**')), 'isdirectory(v:val)'), 's:dirpath(v:val)')
1200 endfunction 1599 endfunction
1217 return extend([printf('x %s:', a:name)], lines) 1616 return extend([printf('x %s:', a:name)], lines)
1218 endif 1617 endif
1219 endfunction 1618 endfunction
1220 1619
1221 function! s:with_cd(cmd, dir) 1620 function! s:with_cd(cmd, dir)
1222 return (s:is_win ? 'cd /d ' : 'cd ').s:esc(a:dir).' && '.a:cmd 1621 return printf('cd%s %s && %s', s:is_win ? ' /d' : '', s:shellesc(a:dir), a:cmd)
1223 endfunction 1622 endfunction
1224 1623
1225 function! s:system(cmd, ...) 1624 function! s:system(cmd, ...)
1226 try 1625 try
1227 let sh = &shell 1626 let sh = &shell
1254 \ 'Expected: '.a:spec.uri, 1653 \ 'Expected: '.a:spec.uri,
1255 \ 'PlugClean required.'], "\n") 1654 \ 'PlugClean required.'], "\n")
1256 let ret = 0 1655 let ret = 0
1257 elseif a:check_branch 1656 elseif a:check_branch
1258 let branch = result[0] 1657 let branch = result[0]
1259 if a:spec.branch !=# branch 1658 " Check tag
1659 if has_key(a:spec, 'tag')
1260 let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) 1660 let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir)
1261 if a:spec.branch !=# tag 1661 if a:spec.tag !=# tag
1262 let msg = printf('Invalid branch/tag: %s (expected: %s). Try PlugUpdate.', 1662 let msg = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.',
1263 \ (empty(tag) ? branch : tag), a:spec.branch) 1663 \ (empty(tag) ? 'N/A' : tag), a:spec.tag)
1264 let ret = 0 1664 let ret = 0
1265 endif 1665 endif
1666 " Check branch
1667 elseif a:spec.branch !=# branch
1668 let msg = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.',
1669 \ branch, a:spec.branch)
1670 let ret = 0
1266 endif 1671 endif
1267 endif 1672 endif
1268 else 1673 else
1269 let msg = 'Not found' 1674 let msg = 'Not found'
1270 let ret = 0 1675 let ret = 0
1342 if v:shell_error 1747 if v:shell_error
1343 throw get(s:lines(output), -1, v:shell_error) 1748 throw get(s:lines(output), -1, v:shell_error)
1344 endif 1749 endif
1345 elseif s:ruby 1750 elseif s:ruby
1346 call s:upgrade_using_ruby(new) 1751 call s:upgrade_using_ruby(new)
1752 elseif s:py2
1753 call s:upgrade_using_python(new)
1347 else 1754 else
1348 return s:err('curl executable or ruby support not found') 1755 return s:err('Missing: curl executable, ruby support or python support')
1349 endif 1756 endif
1350 catch 1757 catch
1351 return s:err('Error upgrading vim-plug: '. v:exception) 1758 return s:err('Error upgrading vim-plug: '. v:exception)
1352 endtry 1759 endtry
1353 1760
1368 ruby << EOF 1775 ruby << EOF
1369 require 'open-uri' 1776 require 'open-uri'
1370 File.open(VIM::evaluate('a:new'), 'w') do |f| 1777 File.open(VIM::evaluate('a:new'), 'w') do |f|
1371 f << open(VIM::evaluate('s:plug_src')).read 1778 f << open(VIM::evaluate('s:plug_src')).read
1372 end 1779 end
1780 EOF
1781 endfunction
1782
1783 function! s:upgrade_using_python(new)
1784 python << EOF
1785 import urllib
1786 import vim
1787 psrc, dest = vim.eval('s:plug_src'), vim.eval('a:new')
1788 urllib.urlretrieve(psrc, dest)
1373 EOF 1789 EOF
1374 endfunction 1790 endfunction
1375 1791
1376 function! s:upgrade_specs() 1792 function! s:upgrade_specs()
1377 for spec in values(g:plugs) 1793 for spec in values(g:plugs)
1486 endif 1902 endif
1487 1903
1488 execute 'pedit' sha 1904 execute 'pedit' sha
1489 wincmd P 1905 wincmd P
1490 setlocal filetype=git buftype=nofile nobuflisted 1906 setlocal filetype=git buftype=nofile nobuflisted
1491 execute 'silent read !cd' s:esc(g:plugs[name].dir) '&& git show' sha 1907 execute 'silent read !cd' s:shellesc(g:plugs[name].dir) '&& git show' sha
1492 normal! gg"_dd 1908 normal! gg"_dd
1493 wincmd p 1909 wincmd p
1494 endfunction 1910 endfunction
1495 1911
1496 function! s:section(flags) 1912 function! s:section(flags)
1520 endif 1936 endif
1521 endfor 1937 endfor
1522 1938
1523 call setline(1, cnt == 0 ? 'No updates.' : 'Last update:') 1939 call setline(1, cnt == 0 ? 'No updates.' : 'Last update:')
1524 nnoremap <silent> <buffer> <cr> :silent! call <SID>preview_commit()<cr> 1940 nnoremap <silent> <buffer> <cr> :silent! call <SID>preview_commit()<cr>
1941 nnoremap <silent> <buffer> o :silent! call <SID>preview_commit()<cr>
1525 nnoremap <silent> <buffer> X :call <SID>revert()<cr> 1942 nnoremap <silent> <buffer> X :call <SID>revert()<cr>
1526 normal! gg 1943 normal! gg
1527 setlocal nomodifiable 1944 setlocal nomodifiable
1528 if cnt > 0 1945 if cnt > 0
1529 echo "Press 'X' on each block to revert the update" 1946 echo "Press 'X' on each block to revert the update"
1547 function! s:snapshot(...) abort 1964 function! s:snapshot(...) abort
1548 let home = get(s:, 'plug_home_org', g:plug_home) 1965 let home = get(s:, 'plug_home_org', g:plug_home)
1549 let [type, var, header] = s:is_win ? 1966 let [type, var, header] = s:is_win ?
1550 \ ['dosbatch', '%PLUG_HOME%', 1967 \ ['dosbatch', '%PLUG_HOME%',
1551 \ ['@echo off', ':: Generated by vim-plug', ':: '.strftime("%c"), '', 1968 \ ['@echo off', ':: Generated by vim-plug', ':: '.strftime("%c"), '',
1552 \ ':: Make sure to PlugUpdate first', '', 'set PLUG_HOME='.s:esc(home)]] : 1969 \ ':: Make sure to PlugUpdate first', '', 'set PLUG_HOME='.home]] :
1553 \ ['sh', '$PLUG_HOME', 1970 \ ['sh', '$PLUG_HOME',
1554 \ ['#!/bin/sh', '# Generated by vim-plug', '# '.strftime("%c"), '', 1971 \ ['#!/bin/sh', '# Generated by vim-plug', '# '.strftime("%c"), '',
1555 \ 'vim +PlugUpdate +qa', '', 'PLUG_HOME='.s:esc(home)]] 1972 \ 'vim +PlugUpdate +qa', '', 'PLUG_HOME='.s:esc(home)]]
1556 1973
1557 call s:prepare() 1974 call s:prepare()
1566 let anchor = line('$') - 1 1983 let anchor = line('$') - 1
1567 for dir in reverse(dirs) 1984 for dir in reverse(dirs)
1568 let sha = s:system_chomp('git rev-parse --short HEAD', dir) 1985 let sha = s:system_chomp('git rev-parse --short HEAD', dir)
1569 if !empty(sha) 1986 if !empty(sha)
1570 call append(anchor, printf('cd %s && git reset --hard %s', 1987 call append(anchor, printf('cd %s && git reset --hard %s',
1571 \ substitute(dir, '^'.g:plug_home, var, ''), sha)) 1988 \ substitute(dir, '^\V'.escape(g:plug_home, '\'), var, ''), sha))
1572 redraw 1989 redraw
1573 endif 1990 endif
1574 endfor 1991 endfor
1575 1992
1576 if a:0 > 0 1993 if a:0 > 0