Skip to content

Commit

Permalink
Neovim Python Support
Browse files Browse the repository at this point in the history
* 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: neovim/pynvim#103
  • Loading branch information
starcraftman committed May 4, 2015
1 parent 2f60637 commit 67a3d3b
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 36 deletions.
5 changes: 2 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -32,6 +30,7 @@ before_script: |
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
vim --version
script: |
test/run !
93 changes: 60 additions & 33 deletions plug.vim
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ 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 = (has('python') || has('python3')) && !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
Expand Down Expand Up @@ -763,6 +763,7 @@ function! s:update_impl(pull, force, args) abort
call s:prepare()
call append(0, ['', ''])
normal! 2G
redraw

let s:clone_opt = get(g:, 'plug_shallow', 1) ?
\ '--depth 1' . (s:git_version_requirement(1, 7, 10) ? ' --no-single-branch' : '') : ''
Expand All @@ -775,6 +776,18 @@ function! s:update_impl(pull, force, args) abort
let s:py = s:version_requirement(
\ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6])
endif

" TODO: Remove when jobwait() blocks correctly
if has('nvim') && has('vim_starting')
if s:py
let s:nvim = 0
else
echohl WarningMsg
echomsg 'vim-plug: +PlugInstall/+PlugUpdate synchronous only with python support'
echomsg 'vim-plug: pip(3) install neovim'
echohl None
endif
endif
if (s:py || s:ruby) && !s:nvim && s:update.threads > 1
try
let imd = &imd
Expand Down Expand Up @@ -1023,22 +1036,22 @@ 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)'))
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):
Expand Down Expand Up @@ -1070,11 +1083,10 @@ class GLog(object):
flog.write(msg.encode())

class Buffer(object):
def __init__(self, lock, num_plugs):
def __init__(self, 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.lock = lock
self.event = 'Updating' if is_pull else 'Installing'
self.is_win = is_win
self.maxy = int(vim.eval('winheight(".")'))
self.num_plugs = num_plugs

Expand Down Expand Up @@ -1103,11 +1115,7 @@ class Buffer(object):
if not self.is_win:
vim.command('redraw')

def write(self, *args, **kwargs):
with self.lock:
self._write(*args, **kwargs)

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]
Expand Down Expand Up @@ -1188,7 +1196,7 @@ class Command(object):
if G_STOP.is_set():
raise KeyboardInterrupt

if first_line or random.random() < G_LOG_PROB:
if random.random() < G_LOG_PROB or first_line:
first_line = False
line = nonblock_read(tfile.name)
if line:
Expand Down Expand Up @@ -1217,10 +1225,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'])
Expand All @@ -1234,7 +1242,7 @@ class Plugin(object):
else:
self.install()
with self.lock:
vim.command("let s:update.new['{0}'] = 1".format(self.name))
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:
Expand All @@ -1258,7 +1266,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))
Expand All @@ -1278,7 +1286,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),
Expand All @@ -1301,7 +1309,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):
Expand All @@ -1311,17 +1319,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):
Expand All @@ -1332,12 +1344,19 @@ class RefreshThread(thr.Thread):
def run(self):
while self.running:
with self.lock:
vim.command('noautocmd normal! a')
vim_command('noautocmd normal! a')
time.sleep(0.2)

def stop(self):
self.running = False

if G_NVIM:
def vim_command(cmd):
vim.session.threadsafe_call(lambda: vim.command(cmd))
else:
def vim_command(cmd):
vim.command(cmd)

def esc(name):
return '"' + name.replace('"', '\"') + '"'

Expand All @@ -1361,7 +1380,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'
Expand All @@ -1371,24 +1389,33 @@ def main():
GLog.write('Num Threads: {0}'.format(nthreads))

lock = thr.Lock()
buf = Buffer(lock, len(plugs))
work_q = queue.Queue()
buf = Buffer(len(plugs), G_PULL, is_win)
work_q, buf_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()
Expand Down

0 comments on commit 67a3d3b

Please sign in to comment.