From 9bee42ca0af74986fb37858fc66b1169af777f3e Mon Sep 17 00:00:00 2001 From: "Jeremy Pallats/starcraft.man" Date: Tue, 7 Apr 2015 23:04:57 -0400 Subject: [PATCH] Neovim Python Support * Buffer updates now managed by __main__ loop with buf_q. * Synchronous neovim install temporarily provided by python installer. * Known issues: * No ctrl-c/interrupt support on nvim. * Graphical bug: https://github.com/neovim/python-client/issues/103 --- .travis.yml | 4 +-- plug.vim | 101 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 61 insertions(+), 44 deletions(-) diff --git a/.travis.yml b/.travis.yml index 88824d4..759dcb0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,19 +7,17 @@ rvm: - 2.1.0 # Test against python3 installer before_script: | + sudo apt-get update -y if [ $(ruby -e 'puts RUBY_VERSION') = 1.9.2 ]; then - sudo apt-get update -y sudo apt-get install -y vim-nox sudo ln -s /usr/bin/vim /usr/local/bin/vim else git clone --depth 1 https://github.com/vim/vim cd vim if [ $(ruby -e 'puts RUBY_VERSION') = 1.9.3 ]; then - sudo apt-get update -y sudo apt-get install -y python2.7-dev ./configure --with-features=huge --enable-pythoninterp elif [ $(ruby -e 'puts RUBY_VERSION') = 2.1.0 ]; then - sudo apt-get update -y sudo apt-get install -y python3-dev ./configure --with-features=huge --enable-python3interp else diff --git a/plug.vim b/plug.vim index f61aadd..d71f070 100644 --- a/plug.vim +++ b/plug.vim @@ -72,9 +72,6 @@ let s:plug_tab = get(s:, 'plug_tab', -1) let s:plug_buf = get(s:, 'plug_buf', -1) let s:mac_gui = has('gui_macvim') && has('gui_running') let s:is_win = has('win32') || has('win64') -let s:py = (has('python') || has('python3')) && !has('nvim') && !s:is_win && !has('win32unix') -let s:py_exe = has('python3') ? 'python3' : 'python' -let s:ruby = has('ruby') && !has('nvim') && (v:version >= 703 || v:version == 702 && has('patch374')) let s:nvim = has('nvim') && exists('*jobwait') && !s:is_win let s:me = resolve(expand(':p')) let s:base_spec = { 'branch': 'master', 'frozen': 0 } @@ -747,6 +744,10 @@ function! s:update_impl(pull, force, args) abort echohl None endif + let python = (has('python') || has('python3')) && !s:is_win && !has('win32unix') + \ && (!s:nvim || has('vim_starting')) + let ruby = has('ruby') && !s:nvim && (v:version >= 703 || v:version == 702 && has('patch374')) + let s:update = { \ 'start': reltime(), \ 'all': todo, @@ -755,7 +756,7 @@ function! s:update_impl(pull, force, args) abort \ 'pull': a:pull, \ 'force': a:force, \ 'new': {}, - \ 'threads': (s:py || s:ruby || s:nvim) ? min([len(todo), threads]) : 1, + \ 'threads': (python || ruby || s:nvim) ? min([len(todo), threads]) : 1, \ 'bar': '', \ 'fin': 0 \ } @@ -768,20 +769,21 @@ function! s:update_impl(pull, force, args) abort \ '--depth 1' . (s:git_version_requirement(1, 7, 10) ? ' --no-single-branch' : '') : '' " Python version requirement (>= 2.7) - if s:py && !has('python3') && !s:ruby && !s:nvim && s:update.threads > 1 + if python && !has('python3') && !ruby && !s:nvim && s:update.threads > 1 redir => pyv silent python import platform; print(platform.python_version()) redir END - let s:py = s:version_requirement( + let python = s:version_requirement( \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6]) endif - if (s:py || s:ruby) && !s:nvim && s:update.threads > 1 + + if (python || ruby) && s:update.threads > 1 try let imd = &imd if s:mac_gui set noimd endif - if s:ruby + if ruby call s:update_ruby() else call s:update_python() @@ -1003,7 +1005,8 @@ endwhile endfunction function! s:update_python() -execute s:py_exe "<< EOF" +let py_exe = has('python3') ? 'python3' : 'python' +execute py_exe "<< EOF" """ Due to use of signals this function is POSIX only. """ import datetime import functools @@ -1023,6 +1026,7 @@ import time import traceback import vim +G_NVIM = vim.eval("has('nvim')") == '1' G_PULL = vim.eval('s:update.pull') == '1' G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)')) @@ -1030,15 +1034,14 @@ G_CLONE_OPT = vim.eval('s:clone_opt') G_PROGRESS = vim.eval('s:progress_opt(1)') G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) G_STOP = thr.Event() +G_THREADS = {} class BaseExc(Exception): def __init__(self, msg): - self.msg = msg - def _get_msg(self): - return self.msg - def _set_msg(self, msg): self._msg = msg - message = property(_get_msg, _set_msg) + @property + def msg(self): + return self._msg class CmdTimedOut(BaseExc): pass class CmdFailed(BaseExc): @@ -1070,10 +1073,10 @@ class GLog(object): flog.write(msg.encode()) class Buffer(object): - def __init__(self, lock, num_plugs): + def __init__(self, lock, num_plugs, is_pull, is_win): self.bar = '' - self.event = 'Updating' if vim.eval('s:update.pull') == '1' else 'Installing' - self.is_win = vim.eval('s:is_win') == '1' + self.event = 'Updating' if is_pull else 'Installing' + self.is_win = is_win self.lock = lock self.maxy = int(vim.eval('winheight(".")')) self.num_plugs = num_plugs @@ -1099,15 +1102,12 @@ class Buffer(object): num_spaces = self.num_plugs - len(self.bar) curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ') - vim.command('normal! 2G') - if not self.is_win: - vim.command('redraw') - - def write(self, *args, **kwargs): with self.lock: - self._write(*args, **kwargs) + vim.command('normal! 2G') + if not self.is_win: + vim.command('redraw') - def _write(self, action, name, lines): + 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] @@ -1217,10 +1217,10 @@ class Command(object): return result class Plugin(object): - def __init__(self, name, args, buf, lock): + def __init__(self, name, args, buf_q, lock): self.name = name self.args = args - self.buf = buf + self.buf_q = buf_q self.lock = lock tag = args.get('tag', 0) self.checkout = esc(tag if tag else args['branch']) @@ -1234,7 +1234,7 @@ class Plugin(object): else: self.install() with self.lock: - vim.command("let s:update.new['{0}'] = 1".format(self.name)) + thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) except (CmdTimedOut, CmdFailed, InvalidURI) as exc: self.write(Action.ERROR, self.name, exc.msg) except KeyboardInterrupt: @@ -1258,7 +1258,7 @@ class Plugin(object): return _clean self.write(Action.INSTALL, self.name, ['Installing ...']) - callback = functools.partial(self.buf.write, Action.INSTALL, self.name) + 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)) @@ -1278,7 +1278,7 @@ class Plugin(object): if G_PULL: self.write(Action.UPDATE, self.name, ['Updating ...']) - callback = functools.partial(self.buf.write, Action.UPDATE, self.name) + callback = functools.partial(self.write, Action.UPDATE, self.name) fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else '' cmds = ['git fetch {0} {1}'.format(fetch_opt, G_PROGRESS), 'git checkout -q {0}'.format(self.checkout), @@ -1301,7 +1301,7 @@ class Plugin(object): def write(self, action, name, msg): GLog.write('{0} {1}: {2}'.format(action, name, '\n'.join(msg))) - self.buf.write(action, name, msg) + self.buf_q.put((action, name, msg)) class PlugThread(thr.Thread): def __init__(self, tname, args): @@ -1311,17 +1311,21 @@ class PlugThread(thr.Thread): def run(self): thr.current_thread().name = self.tname - work_q, lock, buf = self.args + buf_q, work_q, lock = self.args try: while not G_STOP.is_set(): name, args = work_q.get_nowait() GLog.write('{0}: Dir {1}'.format(name, args['dir'])) - plug = Plugin(name, args, buf, lock) + plug = Plugin(name, args, buf_q, lock) plug.manage() work_q.task_done() except queue.Empty: GLog.write('Queue now empty.') + finally: + global G_THREADS + with lock: + del G_THREADS[thr.current_thread().name] class RefreshThread(thr.Thread): def __init__(self, lock): @@ -1332,12 +1336,19 @@ class RefreshThread(thr.Thread): def run(self): while self.running: with self.lock: - vim.command('noautocmd normal! a') + thread_vim_command('noautocmd normal! a') time.sleep(0.2) def stop(self): self.running = False +if G_NVIM: + def thread_vim_command(cmd): + vim.session.threadsafe_call(lambda: vim.command(cmd)) +else: + def thread_vim_command(cmd): + vim.command(cmd) + def esc(name): return '"' + name.replace('"', '\"') + '"' @@ -1361,7 +1372,6 @@ def main(): if GLog.ON and os.path.exists(GLog.LOGDIR): shutil.rmtree(GLog.LOGDIR) - threads = [] nthreads = int(vim.eval('s:update.threads')) plugs = vim.eval('s:update.todo') mac_gui = vim.eval('s:mac_gui') == '1' @@ -1371,24 +1381,33 @@ def main(): GLog.write('Num Threads: {0}'.format(nthreads)) lock = thr.Lock() - buf = Buffer(lock, len(plugs)) - work_q = queue.Queue() + buf = Buffer(lock, len(plugs), G_PULL, is_win) + buf_q, work_q = queue.Queue(), queue.Queue() for work in plugs.items(): work_q.put(work) GLog.write('Starting Threads') + global G_THREADS for num in range(nthreads): tname = 'PlugT-{0:02}'.format(num) - thread = PlugThread(tname, (work_q, lock, buf)) + thread = PlugThread(tname, (buf_q, work_q, lock)) thread.start() - threads.append(thread) + G_THREADS[tname] = thread if mac_gui: rthread = RefreshThread(lock) rthread.start() - GLog.write('Joining Live Threads') - for thread in threads: - thread.join() + GLog.write('Buffer Writing Loop') + while not buf_q.empty() or len(G_THREADS) != 0: + try: + action, name, msg = buf_q.get(True, 0.25) + buf.write(action, name, msg) + buf_q.task_done() + except queue.Empty: + pass + except KeyboardInterrupt: + G_STOP.set() + if mac_gui: rthread.stop() rthread.join()