Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

accept stdin requests and allow answering them by editing the last line #157

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 65 additions & 5 deletions ftplugin/python/vim_ipython.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
monitor_subchannel = True # update vim-ipython 'shell' on every send?
run_flags= "-i" # flags to for IPython's run magic when using <F5>
current_line = ''
allow_stdin = True # whether or not to accept stdin requests
current_stdin_prompt = {}

try:
from queue import Empty # python3 convention
Expand Down Expand Up @@ -355,7 +357,16 @@ def update_subchannel_msgs(debug=False, force=False):
"""
if kc is None or (not vim_ipython_is_open() and not force):
return False

if allow_stdin:
stdin_msgs = kc.stdin_channel.get_msgs()
else:
stdin_msgs = []

msgs = kc.iopub_channel.get_msgs()
msgs += stdin_msgs

global current_stdin_prompt
b = vim.current.buffer
startedin_vimipython = vim.eval('@%')=='vim-ipython'
if not startedin_vimipython:
Expand Down Expand Up @@ -407,6 +418,9 @@ def update_subchannel_msgs(debug=False, force=False):
b = vim.current.buffer
update_occured = False
for m in msgs:
# if we received a message it means the kernel is not waiting for input
vim.command('autocmd! InsertEnter <buffer>')
current_stdin_prompt.clear()
s = ''
if 'msg_type' not in m['header']:
# debug information
Expand Down Expand Up @@ -446,6 +460,13 @@ def update_subchannel_msgs(debug=False, force=False):
c = m['content']
s = "\n".join(map(strip_color_escapes,c['traceback']))
s += c['ename'] + ":" + c['evalue']
elif header == 'input_request':
current_stdin_prompt['prompt'] = m['content']['prompt']
current_stdin_prompt['is_password'] = m['content']['password']
current_stdin_prompt['parent_msg_id'] = m['parent_header']['msg_id']
s += m['content']['prompt']
vim.command('autocmd InsertEnter <buffer> :py InputPrompt()')
echo('Awaiting input. Answer by editing last vim-ipython line')

if s.find('\n') == -1:
# somewhat ugly unicode workaround from
Expand All @@ -460,11 +481,14 @@ def update_subchannel_msgs(debug=False, force=False):
b.append([l.encode(vim_encoding) for l in s.splitlines()])
update_occured = True
# make a newline so we can just start typing there
if status_blank_lines:
if status_blank_lines and not current_stdin_prompt:
if b[-1] != '':
b.append([''])
if update_occured or force:
vim.command('normal! G') # go to the end of the file
if current_stdin_prompt:
vim.command('normal! $') # also go to the end of the line

if not startedin_vimipython:
vim.command('normal! p') # go back to where you were
return update_occured
Expand All @@ -491,7 +515,9 @@ def print_prompt(prompt,msg_id=None):
count = child['content']['execution_count']
echo("In[%d]: %s" %(count,prompt))
except Empty:
echo("In[]: %s (no reply from IPython kernel)" % prompt)
# show empty prompt only if the kernel is not waiting for input
if not kc.stdin_channel.msg_ready():
echo("In[]: %s (no reply from IPython kernel)" % prompt)
else:
echo("In[]: %s" % prompt)

Expand Down Expand Up @@ -534,12 +560,12 @@ def run_this_line(dedent=False):
vim.command('stopi')
w.cursor = original_pos
return
msg_id = send(line)
msg_id = send(line, allow_stdin=allow_stdin)
print_prompt(line, msg_id)

@with_subchannel
def run_command(cmd):
msg_id = send(cmd)
msg_id = send(cmd, allow_stdin=allow_stdin)
print_prompt(cmd, msg_id)

@with_subchannel
Expand All @@ -555,7 +581,7 @@ def run_these_lines(dedent=False):
lines = "\n".join(x[leading:] for x in lines)
else:
lines = "\n".join(vim.current.buffer[r.start:r.end+1])
msg_id = send(lines)
msg_id = send(lines, allow_stdin=allow_stdin)
#alternative way of doing this in more recent versions of ipython
#but %paste only works on the local machine
#vim.command("\"*yy")
Expand All @@ -571,6 +597,40 @@ def run_these_lines(dedent=False):
print_prompt(prompt,msg_id)


@with_subchannel
def InputPrompt():
# If there is a pending input and we are in the last line
if current_stdin_prompt and vim.eval('line(".")')==vim.eval('line("$")'):
vim.command('call inputsave()')
input_command = (
"try"
"|let user_input = {input_command}('{prompt}')"
"|catch /^Vim:Interrupt$/"
"|silent! unlet user_input"
"|endtry"
).format(input_command='inputsecret' if current_stdin_prompt['is_password'] else 'input',
prompt=current_stdin_prompt['prompt'])
vim.command(input_command)
vim.command('call inputrestore()')

if vim.eval('exists("user_input")'):
reply = vim.eval('user_input')
vim.command("silent! unlet user_input")
b = vim.current.buffer
last_line = b[-1]
del b[-1]
b.append((last_line+reply).splitlines())
vim.command('autocmd InsertLeave <buffer> :normal! G$')
vim.command('autocmd InsertLeave <buffer> :autocmd! InsertLeave <buffer>')
vim.command('call feedkeys("\\<Esc>", "n")')
kc.input(reply)
try:
child = get_child_msg(current_stdin_prompt['parent_msg_id'])
except Empty:
pass
current_stdin_prompt.clear()


def set_pid():
"""
Explicitly ask the ipython kernel for its pid
Expand Down