From 089c174d6d47496b5bca5d90989db5e793d78a51 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Thu, 19 Nov 2015 17:58:34 +0900 Subject: [PATCH] Updating plug.vim --- .vim/autoload/plug.vim | 173 ++++++++++++++++++++++++----------------- 1 file changed, 100 insertions(+), 73 deletions(-) diff --git a/.vim/autoload/plug.vim b/.vim/autoload/plug.vim index 9eb11fc..60e6bf5 100644 --- a/.vim/autoload/plug.vim +++ b/.vim/autoload/plug.vim @@ -24,11 +24,14 @@ " " Using git URL " Plug 'https://github.com/junegunn/vim-github-dashboard.git' " +" " Using a non-master branch +" Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' } + " " Plugin options " Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' } " " " Plugin outside ~/.vim/plugged with post-update hook -" Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': 'yes \| ./install' } +" Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } " " " Unmanaged plugin (manually installed and updated) " Plug '~/my-prototype-plugin' @@ -172,11 +175,14 @@ function! plug#end() call s:assoc(lod.map, cmd, name) endif call add(s:triggers[name].map, cmd) - elseif cmd =~ '^[A-Z]' + elseif cmd =~# '^[A-Z]' if exists(':'.cmd) != 2 call s:assoc(lod.cmd, cmd, name) endif call add(s:triggers[name].cmd, cmd) + else + call s:err('Invalid `on` option: '.cmd. + \ '. Should start with an uppercase letter or ``.') endif endfor endif @@ -294,7 +300,7 @@ endif function! s:err(msg) echohl ErrorMsg - echom a:msg + echom '[vim-plug] '.a:msg echohl None return 0 endfunction @@ -646,10 +652,11 @@ function! s:do(pull, force, todo) endif let installed = has_key(s:update.new, name) let updated = installed ? 0 : - \ (a:pull && !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', spec.dir))) + \ (a:pull && index(s:update.errors, name) < 0 && !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', spec.dir))) if a:force || installed || updated execute 'cd' s:esc(spec.dir) call append(3, '- Post-update hook for '. name .' ... ') + let error = '' let type = type(spec.do) if type == s:TYPE.string try @@ -658,21 +665,23 @@ function! s:do(pull, force, todo) let g:_plug_do = '!'.escape(spec.do, '#!%') execute "normal! :execute g:_plug_do\\" finally - let result = v:shell_error ? ('Exit status: '.v:shell_error) : 'Done!' + if v:shell_error + let error = 'Exit status: ' . v:shell_error + endif unlet g:_plug_do endtry elseif type == s:TYPE.funcref try let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged') call spec.do({ 'name': name, 'status': status, 'force': a:force }) - let result = 'Done!' catch - let result = 'Error: ' . v:exception + let error = v:exception endtry else - let result = 'Error: Invalid type!' + let error = 'Invalid hook type' endif - call setline(4, getline(4) . result) + call setline(4, empty(error) ? (getline(4) . 'OK') + \ : ('x' . getline(4)[1:] . error)) cd - endif endfor @@ -1050,17 +1059,17 @@ G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) G_STOP = thr.Event() G_THREADS = {} -class BaseExc(Exception): +class PlugError(Exception): def __init__(self, msg): self._msg = msg @property def msg(self): return self._msg -class CmdTimedOut(BaseExc): +class CmdTimedOut(PlugError): pass -class CmdFailed(BaseExc): +class CmdFailed(PlugError): pass -class InvalidURI(BaseExc): +class InvalidURI(PlugError): pass class Action(object): INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] @@ -1074,7 +1083,7 @@ class Buffer(object): self.maxy = int(vim.eval('winheight(".")')) self.num_plugs = num_plugs - def _where(self, name): + def __where(self, name): """ Find first line with name in current buffer. Return line num. """ found, lnum = False, 0 matcher = re.compile('^[-+x*] {0}:'.format(name)) @@ -1103,8 +1112,7 @@ class Buffer(object): def write(self, action, name, lines): first, rest = lines[0], lines[1:] msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)] - padded_rest = [' ' + line for line in rest] - msg.extend(padded_rest) + msg.extend([' ' + line for line in rest]) try: if action == Action.ERROR: @@ -1114,7 +1122,7 @@ class Buffer(object): self.bar += '=' curbuf = vim.current.buffer - lnum = self._where(name) + lnum = self.__where(name) if lnum != -1: # Found matching line num del curbuf[lnum] if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]): @@ -1128,56 +1136,73 @@ class Buffer(object): pass class Command(object): - def __init__(self, cmd, cmd_dir=None, timeout=60, ntries=3, cb=None, clean=None): + def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None): self.cmd = cmd self.cmd_dir = cmd_dir self.timeout = timeout - self.ntries = ntries self.callback = cb if cb else (lambda msg: None) - self.clean = clean + self.clean = clean if clean else (lambda: None) + self.proc = None - def attempt_cmd(self): - """ Tries to run the command, returns result if no exceptions. """ - attempt = 0 - finished = False - limit = self.timeout + @property + def alive(self): + """ Returns true only if command still running. """ + return self.proc and self.proc.poll() is None + + def execute(self, ntries=3): + """ Execute the command with ntries if CmdTimedOut. + Returns the output of the command if no Exception. + """ + attempt, finished, limit = 0, False, self.timeout while not finished: try: attempt += 1 - result = self.timeout_cmd() + result = self.try_command() finished = True + return result except CmdTimedOut: - if attempt != self.ntries: - for count in range(3, 0, -1): - if G_STOP.is_set(): - raise KeyboardInterrupt - msg = 'Timeout. Will retry in {0} second{1} ...'.format( - count, 's' if count != 1 else '') - self.callback([msg]) - time.sleep(1) + if attempt != ntries: + self.notify_retry() self.timeout += limit - self.callback(['Retrying ...']) else: raise - return result + def notify_retry(self): + """ Retry required for command, notify user. """ + for count in range(3, 0, -1): + if G_STOP.is_set(): + raise KeyboardInterrupt + msg = 'Timeout. Will retry in {0} second{1} ...'.format( + count, 's' if count != 1 else '') + self.callback([msg]) + time.sleep(1) + self.callback(['Retrying ...']) - def timeout_cmd(self): + def try_command(self): """ Execute a cmd & poll for callback. Returns list of output. - Raises CmdFailed -> return code for Popen isn't 0 - Raises CmdTimedOut -> command exceeded timeout without new output + Raises CmdFailed -> return code for Popen isn't 0 + Raises CmdTimedOut -> command exceeded timeout without new output """ - proc = None first_line = True - try: - tfile = tempfile.NamedTemporaryFile(mode='w+b', delete=False) - proc = subprocess.Popen(self.cmd, cwd=self.cmd_dir, stdout=tfile, - stderr=subprocess.STDOUT, shell=True, preexec_fn=os.setsid) - while proc.poll() is None: - # Yield this thread - time.sleep(0.2) + try: + tfile = tempfile.NamedTemporaryFile(mode='w+b') + self.proc = subprocess.Popen(self.cmd, cwd=self.cmd_dir, stdout=tfile, + stderr=subprocess.STDOUT, shell=True, + preexec_fn=os.setsid) + thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,)) + thrd.start() + + thread_not_started = True + while thread_not_started: + try: + thrd.join(0.1) + thread_not_started = False + except RuntimeError: + pass + + while self.alive: if G_STOP.is_set(): raise KeyboardInterrupt @@ -1191,23 +1216,24 @@ class Command(object): if time_diff > self.timeout: raise CmdTimedOut(['Timeout!']) + thrd.join(0.5) + tfile.seek(0) result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] - if proc.returncode != 0: - msg = [''] - msg.extend(result) - raise CmdFailed(msg) - except: - if proc and proc.poll() is None: - os.killpg(proc.pid, signal.SIGTERM) - if self.clean: - self.clean() - raise - finally: - os.remove(tfile.name) + if self.proc.returncode != 0: + raise CmdFailed([''] + result) - return result + return result + except: + self.terminate() + raise + + def terminate(self): + """ Terminate process and cleanup. """ + if self.alive: + os.killpg(self.proc.pid, signal.SIGTERM) + self.clean() class Plugin(object): def __init__(self, name, args, buf_q, lock): @@ -1228,7 +1254,7 @@ class Plugin(object): self.install() with self.lock: thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) - except (CmdTimedOut, CmdFailed, InvalidURI) as exc: + except PlugError as exc: self.write(Action.ERROR, self.name, exc.msg) except KeyboardInterrupt: G_STOP.set() @@ -1253,11 +1279,18 @@ class Plugin(object): self.write(Action.INSTALL, self.name, ['Installing ...']) callback = functools.partial(self.write, Action.INSTALL, self.name) cmd = 'git clone {0} {1} --recursive {2} -b {3} {4} 2>&1'.format( - '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], self.checkout, esc(target)) - com = Command(cmd, None, G_TIMEOUT, G_RETRIES, callback, clean(target)) - result = com.attempt_cmd() + '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], + self.checkout, esc(target)) + com = Command(cmd, None, G_TIMEOUT, callback, clean(target)) + result = com.execute(G_RETRIES) self.write(Action.DONE, self.name, result[-1:]) + def repo_uri(self): + cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url' + command = Command(cmd, self.args['dir'], G_TIMEOUT,) + result = command.execute(G_RETRIES) + return result[-1] + def update(self): match = re.compile(r'git::?@') actual_uri = re.sub(match, '', self.repo_uri()) @@ -1278,18 +1311,12 @@ class Plugin(object): 'git merge --ff-only {0}'.format(self.merge), 'git submodule update --init --recursive'] cmd = ' 2>&1 && '.join(cmds) - com = Command(cmd, self.args['dir'], G_TIMEOUT, G_RETRIES, callback) - result = com.attempt_cmd() + com = Command(cmd, self.args['dir'], G_TIMEOUT, callback) + result = com.execute(G_RETRIES) self.write(Action.DONE, self.name, result[-1:]) else: self.write(Action.DONE, self.name, ['Already installed']) - def repo_uri(self): - cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url' - command = Command(cmd, self.args['dir'], G_TIMEOUT, G_RETRIES) - result = command.attempt_cmd() - return result[-1] - def write(self, action, name, msg): self.buf_q.put((action, name, msg)) @@ -1326,7 +1353,7 @@ class RefreshThread(thr.Thread): while self.running: with self.lock: thread_vim_command('noautocmd normal! a') - time.sleep(0.2) + time.sleep(0.33) def stop(self): self.running = False