From 6d30bfdc7bfe6a08f36dcab571cb016d3342bb9d Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 14 Sep 2014 17:54:01 -0700 Subject: [PATCH 001/122] Add support for NeoComplete by setting omnifunc instead of completefunc --- README.rst | 6 ++-- .../neocomplete/sources/ipythoncomplete.vim | 29 +++++++++++++++++++ ftplugin/python/ipy.vim | 1 + ftplugin/python/vim_ipython.py | 2 ++ 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 autoload/neocomplete/sources/ipythoncomplete.vim diff --git a/README.rst b/README.rst index c7ff5f2..e100358 100644 --- a/README.rst +++ b/README.rst @@ -170,8 +170,10 @@ vim-ipython from defining any of the default mappings. By default, vim-ipython activates the custom completefunc globally. Sometimes, having a completefunc breaks other plugins' completions. Putting the line ``let g:ipy_completefunc = 'local'`` in one's vimrc will activate the -IPython-based completion only for current buffer. Setting `g:ipy_completefunc` -to anything other than `'local'` or `'global'` disables it altogether. +IPython-based completion only for current buffer. Putting ``let +g:ipy_completefunc = 'omni'`` will set the omnifunc option for the current +buffer. Setting `g:ipy_completefunc` to anything other than `'local'` or +`'global'` disables it altogether. **NEW since IPython 0.13** diff --git a/autoload/neocomplete/sources/ipythoncomplete.vim b/autoload/neocomplete/sources/ipythoncomplete.vim new file mode 100644 index 0000000..e812f8c --- /dev/null +++ b/autoload/neocomplete/sources/ipythoncomplete.vim @@ -0,0 +1,29 @@ +let s:save_cpo = &cpo +set cpo&vim + +let s:source = { + \ 'name' : 'ipython-complete', + \ 'kind' : 'keyword', + \ 'mark' : '[IPy]', + \ 'rank' : 4, + \ } + +function! neocomplete#sources#ipythoncomplete(findstart, base) + if !exists('*CompleteIPython') + return + else + return CompleteIPython(a:findstart, a:base) + endif +endfunction + +function! s:source.gather_candidates(context) + return neocomplete#sources#ipythoncomplete(0, '') +endfunction + +function! neocomplete#sources#tmuxcomplete#define() + return s:source +endfunction + +let &cpo = s:save_cpo +unlet s:save_cpo + diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 8c7011b..3d85776 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -32,6 +32,7 @@ endif " Register IPython completefunc " 'global' -- for all of vim (default). " 'local' -- only for the current buffer. +" 'omni' -- set omnifunc for current buffer. " otherwise -- don't register it at all. " " you can later set it using ':set completefunc=CompleteIPython', which will diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 202925c..fdda269 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -197,6 +197,8 @@ def km_from_string(s=''): set completefunc=CompleteIPython elseif g:ipy_completefunc == 'local' setl completefunc=CompleteIPython + elseif g:ipy_completefunc == 'omni' + setl omnifunc=CompleteIPython endif """) # also activate GUI doc balloons if in gvim From af8444e30fbcc8752950d6afd710b8891f26a862 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 14 Sep 2014 18:29:34 -0700 Subject: [PATCH 002/122] Fix neocomplete function names; check has('python'); only use CompleteIPython if it is the current omnifunc --- autoload/neocomplete/sources/ipythoncomplete.vim | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/autoload/neocomplete/sources/ipythoncomplete.vim b/autoload/neocomplete/sources/ipythoncomplete.vim index e812f8c..9981cbc 100644 --- a/autoload/neocomplete/sources/ipythoncomplete.vim +++ b/autoload/neocomplete/sources/ipythoncomplete.vim @@ -1,3 +1,7 @@ +if !has('python') + finish +endif + let s:save_cpo = &cpo set cpo&vim @@ -8,19 +12,19 @@ let s:source = { \ 'rank' : 4, \ } -function! neocomplete#sources#ipythoncomplete(findstart, base) - if !exists('*CompleteIPython') - return - else +function! neocomplete#sources#ipythoncomplete#complete(findstart, base) + if exists('*CompleteIPython') && &l:omnifunc == 'CompleteIPython' return CompleteIPython(a:findstart, a:base) + else + return endif endfunction function! s:source.gather_candidates(context) - return neocomplete#sources#ipythoncomplete(0, '') + return neocomplete#sources#ipythoncomplete#complete(0, '') endfunction -function! neocomplete#sources#tmuxcomplete#define() +function! neocomplete#sources#ipythoncomplete#define() return s:source endfunction From c2c09897297255509b28e017dde87045cc9cdb8f Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sat, 20 Sep 2014 16:05:43 -0700 Subject: [PATCH 003/122] Use CompleteIPython in neocomplete even if it's not the current omnifunc (but wrap in try in case it's not connected) --- autoload/neocomplete/sources/ipythoncomplete.vim | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/autoload/neocomplete/sources/ipythoncomplete.vim b/autoload/neocomplete/sources/ipythoncomplete.vim index 9981cbc..197aedf 100644 --- a/autoload/neocomplete/sources/ipythoncomplete.vim +++ b/autoload/neocomplete/sources/ipythoncomplete.vim @@ -13,10 +13,11 @@ let s:source = { \ } function! neocomplete#sources#ipythoncomplete#complete(findstart, base) - if exists('*CompleteIPython') && &l:omnifunc == 'CompleteIPython' - return CompleteIPython(a:findstart, a:base) - else - return + if &filetype == 'python' + " Wrap in try statement in case IPython is not connected + try + return CompleteIPython(a:findstart, a:base) + endtry endif endfunction From b819b3fa507406b14c5264db9d13bdb77d72257b Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sat, 20 Sep 2014 16:28:26 -0700 Subject: [PATCH 004/122] Suppress errors using silent! instead of try so neocomplete doesn't give warning --- autoload/neocomplete/sources/ipythoncomplete.vim | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/autoload/neocomplete/sources/ipythoncomplete.vim b/autoload/neocomplete/sources/ipythoncomplete.vim index 197aedf..6253f01 100644 --- a/autoload/neocomplete/sources/ipythoncomplete.vim +++ b/autoload/neocomplete/sources/ipythoncomplete.vim @@ -14,10 +14,7 @@ let s:source = { function! neocomplete#sources#ipythoncomplete#complete(findstart, base) if &filetype == 'python' - " Wrap in try statement in case IPython is not connected - try - return CompleteIPython(a:findstart, a:base) - endtry + silent! return CompleteIPython(a:findstart, a:base) endif endfunction From 254ec1a266cbf06e7edd48de9fadc69f7ac83821 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 4 Dec 2014 19:21:23 -0700 Subject: [PATCH 005/122] Ignore .pyc files --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc From 9fb23da063a1116ca8602d51637b1f7dad55f369 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 4 Dec 2014 19:23:01 -0700 Subject: [PATCH 006/122] Move autocmds into vim-ipython augroup --- ftplugin/python/ipy.vim | 60 ++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 3d85776..db22cf6 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -63,34 +63,38 @@ fun! toggle_send_on_save() endif endfun -" Update the vim-ipython shell when the cursor is not moving. -" You can change how quickly this happens after you stop moving the cursor by -" setting 'updatetime' (in milliseconds). For example, to have this event -" trigger after 1 second: -" -" :set updatetime 1000 -" -" NOTE: This will only be triggered once, after the first 'updatetime' -" milliseconds, *not* every 'updatetime' milliseconds. see :help CursorHold -" for more info. -" -" TODO: Make this easily configurable on the fly, so that an introspection -" buffer we may have opened up doesn't get closed just because of an idle -" event (i.e. user pressed \d and then left the buffer that popped up, but -" expects it to stay there). -au CursorHold *.*,vim-ipython :python if update_subchannel_msgs(): echo("vim-ipython shell updated (on idle)",'Operator') - -" XXX: broken - cursor hold update for insert mode moves the cursor one -" character to the left of the last character (update_subchannel_msgs must be -" doing this) -"au CursorHoldI *.* :python if update_subchannel_msgs(): echo("vim-ipython shell updated (on idle)",'Operator') - -" Same as above, but on regaining window focus (mostly for GUIs) -au FocusGained *.*,vim-ipython :python if update_subchannel_msgs(): echo("vim-ipython shell updated (on input focus)",'Operator') - -" Update vim-ipython buffer when we move the cursor there. A message is only -" displayed if vim-ipython buffer has been updated. -au BufEnter vim-ipython :python if update_subchannel_msgs(): echo("vim-ipython shell updated (on buffer enter)",'Operator') +augroup vim-ipython + autocmd! + au FileType python IPython + " Update the vim-ipython shell when the cursor is not moving. + " You can change how quickly this happens after you stop moving the cursor by + " setting 'updatetime' (in milliseconds). For example, to have this event + " trigger after 1 second: + " + " :set updatetime 1000 + " + " NOTE: This will only be triggered once, after the first 'updatetime' + " milliseconds, *not* every 'updatetime' milliseconds. see :help CursorHold + " for more info. + " + " TODO: Make this easily configurable on the fly, so that an introspection + " buffer we may have opened up doesn't get closed just because of an idle + " event (i.e. user pressed \d and then left the buffer that popped up, but + " expects it to stay there). + au CursorHold *.*,vim-ipython :python if update_subchannel_msgs(): echo("vim-ipython shell updated (on idle)",'Operator') + + " XXX: broken - cursor hold update for insert mode moves the cursor one + " character to the left of the last character (update_subchannel_msgs must be + " doing this) + "au CursorHoldI *.* :python if update_subchannel_msgs(): echo("vim-ipython shell updated (on idle)",'Operator') + + " Same as above, but on regaining window focus (mostly for GUIs) + au FocusGained *.*,vim-ipython :python if update_subchannel_msgs(): echo("vim-ipython shell updated (on input focus)",'Operator') + + " Update vim-ipython buffer when we move the cursor there. A message is only + " displayed if vim-ipython buffer has been updated. + au BufEnter vim-ipython :python if update_subchannel_msgs(): echo("vim-ipython shell updated (on buffer enter)",'Operator') +augroup END " Setup plugin mappings for the most common ways to interact with ipython. noremap (IPython-RunFile) :python run_this_file() From 7f20e03972fa1d733d91c94926b834146f8a678b Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 4 Dec 2014 19:26:02 -0700 Subject: [PATCH 007/122] Move mappings into function and run automatically --- ftplugin/python/ipy.vim | 87 ++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index db22cf6..3fb3750 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -29,6 +29,12 @@ if !exists('g:ipy_perform_mappings') let g:ipy_perform_mappings = 1 endif +" Automatically run :IPython in python files after running :IPython the first +" time +if !exists('g:ipy_autostart') + let g:ipy_autostart = 1 +endif + " Register IPython completefunc " 'global' -- for all of vim (default). " 'local' -- only for the current buffer. @@ -114,42 +120,53 @@ noremap (IPython-PlotCloseAll) :python run_command("plt.close('all' noremap (IPython-RunLineAsTopLevel) :python dedent_run_this_line() xnoremap (IPython-RunLinesAsTopLevel) :python dedent_run_these_lines() -if g:ipy_perform_mappings != 0 - map (IPython-RunFile) - map (IPython-RunLine) - map (IPython-RunLines) - map d (IPython-OpenPyDoc) - map s (IPython-UpdateShell) - map (IPython-ToggleReselect) - "map (IPython-StartDebugging) - "map (IPython-BreakpointSet) - "map (IPython-BreakpointClear) - "map (IPython-DebugThisFile) - "map (IPython-BreakpointClearAll) - imap (IPython-RunFile) - imap (IPython-RunLines) - imap (IPython-RunFile) - map (IPython-ToggleSendOnSave) - "" Example of how to quickly clear the current plot with a keystroke - "map (IPython-PlotClearCurrent) - "" Example of how to quickly close all figures with a keystroke - "map (IPython-PlotCloseAll) - - "pi custom - map (IPython-RunFile) - map (IPython-RunLine) - imap (IPython-RunLine) - map (IPython-RunLineAsTopLevel) - xmap (IPython-RunLines) - xmap (IPython-RunLinesAsTopLevel) - - noremap I# - xnoremap I# - noremap :s/^\([ \t]*\)#/\1/ - xnoremap :s/^\([ \t]*\)#/\1/ -endif +function! s:DoMappings() + let b:did_ipython = 1 + if g:ipy_perform_mappings != 0 + map (IPython-RunFile) + map (IPython-RunLine) + map (IPython-RunLines) + map d (IPython-OpenPyDoc) + map s (IPython-UpdateShell) + map (IPython-ToggleReselect) + "map (IPython-StartDebugging) + "map (IPython-BreakpointSet) + "map (IPython-BreakpointClear) + "map (IPython-DebugThisFile) + "map (IPython-BreakpointClearAll) + imap (IPython-RunFile) + imap (IPython-RunLines) + imap (IPython-RunFile) + map (IPython-ToggleSendOnSave) + "" Example of how to quickly clear the current plot with a keystroke + "map (IPython-PlotClearCurrent) + "" Example of how to quickly close all figures with a keystroke + "map (IPython-PlotCloseAll) + + "pi custom + map (IPython-RunFile) + map (IPython-RunLine) + imap (IPython-RunLine) + map (IPython-RunLineAsTopLevel) + xmap (IPython-RunLines) + xmap (IPython-RunLinesAsTopLevel) + + noremap I# + xnoremap I# + noremap :s/^\([ \t]*\)#/\1/ + xnoremap :s/^\([ \t]*\)#/\1/ + endif + + augroup vim_ipython_autostart + autocmd! + autocmd BufEnter *.py,--Python-- if g:ipy_autostart && !exists('b:did_ipython') + \ | call s:DoMappings() | endif + augroup END + + setlocal omnifunc=CompleteIPython +endfunction -command! -nargs=* IPython :py km_from_string("") +command! -nargs=* IPython :call DoMappings()|:py km_from_string("") command! -nargs=0 IPythonClipboard :py km_from_string(vim.eval('@+')) command! -nargs=0 IPythonXSelection :py km_from_string(vim.eval('@*')) command! -nargs=* IPythonNew :py new_ipy("") From 32da7774d47af3783e376dde004a78056c5f7725 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 4 Dec 2014 19:26:52 -0700 Subject: [PATCH 008/122] Tweak mappings and add get_doc_buffer --- ftplugin/python/ipy.vim | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 3fb3750..3fc233c 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -103,7 +103,7 @@ augroup vim-ipython augroup END " Setup plugin mappings for the most common ways to interact with ipython. -noremap (IPython-RunFile) :python run_this_file() +noremap (IPython-RunFile) :update:python run_this_file() noremap (IPython-RunLine) :python run_this_line() noremap (IPython-RunLines) :python run_these_lines() noremap (IPython-OpenPyDoc) :python get_doc_buffer() @@ -123,11 +123,13 @@ xnoremap (IPython-RunLinesAsTopLevel) :python dedent_run_these_lines() function! s:DoMappings() let b:did_ipython = 1 if g:ipy_perform_mappings != 0 + if &buftype == '' map (IPython-RunFile) + endif map (IPython-RunLine) map (IPython-RunLines) - map d (IPython-OpenPyDoc) - map s (IPython-UpdateShell) + map ,d (IPython-OpenPyDoc) + map (IPython-UpdateShell) map (IPython-ToggleReselect) "map (IPython-StartDebugging) "map (IPython-BreakpointSet) @@ -136,7 +138,7 @@ function! s:DoMappings() "map (IPython-BreakpointClearAll) imap (IPython-RunFile) imap (IPython-RunLines) - imap (IPython-RunFile) + " imap (IPython-RunFile) map (IPython-ToggleSendOnSave) "" Example of how to quickly clear the current plot with a keystroke "map (IPython-PlotClearCurrent) @@ -145,16 +147,20 @@ function! s:DoMappings() "pi custom map (IPython-RunFile) - map (IPython-RunLine) - imap (IPython-RunLine) - map (IPython-RunLineAsTopLevel) - xmap (IPython-RunLines) - xmap (IPython-RunLinesAsTopLevel) - - noremap I# - xnoremap I# - noremap :s/^\([ \t]*\)#/\1/ - xnoremap :s/^\([ \t]*\)#/\1/ + " map x (IPython-RunLine) + imap x (IPython-RunLine) + map (IPython-RunLineAsTopLevel) + xmap x (IPython-RunLines) + xmap (IPython-RunLinesAsTopLevel) + map x (IPython-RunCell) + + " noremap I# + " xnoremap I# + " noremap :s/^\([ \t]*\)#/\1/ + " xnoremap :s/^\([ \t]*\)#/\1/ + + nnoremap :IPythonInterrupt + inoremap :call GetDocBuffer() endif augroup vim_ipython_autostart @@ -166,6 +172,13 @@ function! s:DoMappings() setlocal omnifunc=CompleteIPython endfunction +function! s:GetDocBuffer() + python get_doc_buffer() + nnoremap gi ZQ:undojoinstartinsert! + nnoremap q ZQ:undojoinstartinsert! + nnoremap ` p:if winheight(0)<30res 30endifundojoinstartinsert! +endfunction + command! -nargs=* IPython :call DoMappings()|:py km_from_string("") command! -nargs=0 IPythonClipboard :py km_from_string(vim.eval('@+')) command! -nargs=0 IPythonXSelection :py km_from_string(vim.eval('@*')) From a0b8227b644b86fd5872e562e1b8a672331fe8a0 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 4 Dec 2014 19:27:57 -0700 Subject: [PATCH 009/122] Dictionary and greedy completion with metadata --- ftplugin/python/ipy.vim | 56 ++++++++++++++++++++++++++++------ ftplugin/python/vim_ipython.py | 13 +++++--- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 3fc233c..d7fd1cc 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -29,6 +29,13 @@ if !exists('g:ipy_perform_mappings') let g:ipy_perform_mappings = 1 endif +if !exists('g:ipython_dictionary_completion') + let g:ipython_dictionary_completion = 0 +endif +if !exists('g:ipython_greedy_matching') + let g:ipython_greedy_matching = 0 +endif + " Automatically run :IPython in python files after running :IPython the first " time if !exists('g:ipy_autostart') @@ -45,7 +52,7 @@ endif " correspond to the 'global' behavior, or with ':setl ...' to get the 'local' " behavior if !exists('g:ipy_completefunc') - let g:ipy_completefunc = 'global' + let g:ipy_completefunc = 'omni' endif python << EOF @@ -195,26 +202,38 @@ endpython return l:doc endfunction +if g:ipython_greedy_matching + let s:split_pattern = "[^= \r\n().]" +else + let s:split_pattern = '\k\|\.' +endif + fun! CompleteIPython(findstart, base) if a:findstart " locate the start of the word - let line = getline('.') - let start = col('.') - 1 - while start > 0 && line[start-1] =~ '\k\|\.' "keyword - let start -= 1 + let line = getline('.')[:col('.')-1] + let s:start = col('.') - 1 + if line[s:start-1] !~ s:split_pattern && + \ !(g:ipython_greedy_matching && s:start >= 2 + \ && line[s:start-2] =~ '\k') + return -1 + endif + while s:start > 0 && (line[s:start-1] =~ s:split_pattern + \ || (g:ipython_greedy_matching && line[s:start-1] == '.' + \ && s:start >= 2 && line[s:start-2] =~ '\k')) + let s:start -= 1 endwhile - echo start python << endpython current_line = vim.current.line endpython - return start + return s:start else " find months matching with "a:base" let res = [] python << endpython base = vim.eval("a:base") findstart = vim.eval("a:findstart") -matches = ipy_complete(base, current_line, vim.eval("col('.')")) +matches, metadata = ipy_complete(base, current_line, int(vim.eval('s:start')) + len(base)) # we need to be careful with unicode, because we can have unicode # completions for filenames (for the %run magic, for example). So the next # line will fail on those: @@ -222,6 +241,15 @@ matches = ipy_complete(base, current_line, vim.eval("col('.')")) # because str() won't work for non-ascii characters # and we also have problems with unicode in vim, hence the following: completions = [s.encode(vim_encoding) for s in matches] +metadata = [s.encode(vim_encoding) for s in metadata] +if vim.vars['ipython_dictionary_completion'] and not vim.vars['ipython_greedy_matching']: + for c in completions: + if c.endswith("']"): + completions = filter(lambda c: c.endswith("']"), completions) + break + elif c.endswith('"]'): + completions = filter(lambda c: c.endswith('"]'), completions) + break ## Additionally, we have no good way of communicating lists to vim, so we have ## to turn in into one long string, which can be problematic if e.g. the ## completions contain quotes. The next line will not work if some filenames @@ -233,8 +261,16 @@ completions = [s.encode(vim_encoding) for s in matches] ## if there's a problem with turning a match into a string, it'll just not ## include the problematic match, instead of not including anything. There's a ## bit more indirection here, but I think it's worth it -for c in completions: - vim.command('call add(res,"'+c+'")') +try: + completions, metadata = zip(*sorted(zip(completions, metadata), key=lambda x: x[0].lower())) +except ValueError: + pass +for c, m in zip(completions, metadata): + if 'CALLSIG' in m: + split = m.partition('CALLSIG') + vim.command('call add(res, {"word": "'+c.replace('"', r'\"')+'", "menu": "'+split[0].replace('"', r'\"')+'", "info": "'+split[-1].replace('"', r'\"')+'"})') + else: + vim.command('call add(res, {"word": "'+c.replace('"', r'\"')+'", "menu": "'+m.replace('"', r'\"')+'", "info": ""})') endpython "call extend(res,completions) return res diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index fdda269..7e1a3a5 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -302,21 +302,24 @@ def get_doc_buffer(level=0): vim.command('setlocal syntax=python') def ipy_complete(base, current_line, pos): - msg_id = kc.shell_channel.complete(base, current_line, pos) + msg_id = kc.shell_channel.complete( + base, + current_line[pos-len(base):pos], + len(base)) try: - m = get_child_msg(msg_id) + m = get_child_msg(msg_id, timeout=2) matches = m['content']['matches'] - matches.insert(0,base) # the "no completion" version + metadata = m['content']['metadata'] # we need to be careful with unicode, because we can have unicode # completions for filenames (for the %run magic, for example). So the next # line will fail on those: #completions= [str(u) for u in matches] # because str() won't work for non-ascii characters # and we also have problems with unicode in vim, hence the following: - return matches + return matches, metadata except Empty: echo("no reply from IPython kernel") - return [''] + return [''], [''] def vim_ipython_is_open(): """ From 5fca252ef2d30b6b6d5ed49281ae4ee3734e1338 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 4 Dec 2014 19:29:27 -0700 Subject: [PATCH 010/122] Improve km_from_string function --- ftplugin/python/vim_ipython.py | 153 ++++++++++++++++++++------------- 1 file changed, 94 insertions(+), 59 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 7e1a3a5..97f6c61 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -19,6 +19,7 @@ def __getattribute__(self, key): print("uh oh, not running inside vim") import sys +import time # get around unicode problems when interfacing with vim vim_encoding=vim.eval('&encoding') or 'utf-8' @@ -125,68 +126,103 @@ def km_from_string(s=''): global km, kc, send - s = s.replace('--existing', '') - if 'connection_file' in KernelManager.class_trait_names(): - # 0.12 uses files instead of a collection of ports - # include default IPython search path - # filefind also allows for absolute paths, in which case the search - # is ignored + # Test if connection is still alive + connected = False + starttime = time.time() + attempt = 0 + while not connected and (time.time() - starttime) < 5.0: + attempt += 1 try: - # XXX: the following approach will be brittle, depending on what - # connection strings will end up looking like in the future, and - # whether or not they are allowed to have spaces. I'll have to sync - # up with the IPython team to address these issues -pi - if '--profile' in s: - k,p = s.split('--profile') - k = k.lstrip().rstrip() # kernel part of the string - p = p.lstrip().rstrip() # profile part of the string - fullpath = find_connection_file(k,p) - else: - fullpath = find_connection_file(s.lstrip().rstrip()) - except IOError as e: - echo(":IPython " + s + " failed", "Info") - echo("^-- failed '" + s + "' not found", "Error") - return - km = KernelManager(connection_file = fullpath) + fullpath = find_connection_file('kernel*') + except IOError: + echo("IPython connection attempt #%d failed - no kernel file" % attempt, "Warning") + time.sleep(1) + continue + + km = KernelManager(connection_file=fullpath) km.load_connection_file() - else: - if s == '': - echo(":IPython 0.11 requires the full connection string") - return - loader = KeyValueConfigLoader(s.split(), aliases=kernel_aliases) - cfg = loader.load_config()['KernelApp'] - try: - km = KernelManager( - shell_address=(ip, cfg['shell_port']), - sub_address=(ip, cfg['iopub_port']), - stdin_address=(ip, cfg['stdin_port']), - hb_address=(ip, cfg['hb_port'])) - except KeyError as e: - echo(":IPython " +s + " failed", "Info") - echo("^-- failed --"+e.message.replace('_port','')+" not specified", "Error") - return - try: kc = km.client() - except AttributeError: - # 0.13 - kc = km - kc.start_channels() - send = kc.shell_channel.execute - - #XXX: backwards compatibility for IPython < 0.13 - import inspect - sc = kc.shell_channel - num_oinfo_args = len(inspect.getargspec(sc.object_info).args) - if num_oinfo_args == 2: - # patch the object_info method which used to only take one argument - klass = sc.__class__ - klass._oinfo_orig = klass.object_info - klass.object_info = lambda s,x,y: s._oinfo_orig(x) - - #XXX: backwards compatibility for IPython < 1.0 - if not hasattr(kc, 'iopub_channel'): - kc.iopub_channel = kc.sub_channel + kc.start_channels() + + s = s.replace('--existing', '') + if 'connection_file' in KernelManager.class_trait_names(): + # 0.12 uses files instead of a collection of ports + # include default IPython search path + # filefind also allows for absolute paths, in which case the search + # is ignored + try: + # XXX: the following approach will be brittle, depending on what + # connection strings will end up looking like in the future, and + # whether or not they are allowed to have spaces. I'll have to sync + # up with the IPython team to address these issues -pi + if '--profile' in s: + k,p = s.split('--profile') + k = k.lstrip().rstrip() # kernel part of the string + p = p.lstrip().rstrip() # profile part of the string + fullpath = find_connection_file(k,p) + else: + fullpath = find_connection_file(s.lstrip().rstrip()) + except IOError as e: + echo(":IPython " + s + " failed", "Info") + echo("^-- failed '" + s + "' not found", "Error") + return + km = KernelManager(connection_file = fullpath) + km.load_connection_file() + else: + if s == '': + echo(":IPython 0.11 requires the full connection string") + return + loader = KeyValueConfigLoader(s.split(), aliases=kernel_aliases) + cfg = loader.load_config()['KernelApp'] + try: + km = KernelManager( + shell_address=(ip, cfg['shell_port']), + sub_address=(ip, cfg['iopub_port']), + stdin_address=(ip, cfg['stdin_port']), + hb_address=(ip, cfg['hb_port'])) + except KeyError as e: + echo(":IPython " +s + " failed", "Info") + echo("^-- failed --"+e.message.replace('_port','')+" not specified", "Error") + return + + try: + kc = km.client() + except AttributeError: + # 0.13 + kc = km + kc.start_channels() + send = kc.shell_channel.execute + + kc.shell_channel.execute('', silent=True) + try: + msg = kc.shell_channel.get_msg(timeout=1) + connected = True + except: + echo("IPython connection attempt #%d failed - no messages" % attempt, "Warning") + continue + + #XXX: backwards compatibility for IPython < 0.13 + import inspect + sc = kc.shell_channel + num_oinfo_args = len(inspect.getargspec(sc.object_info).args) + if num_oinfo_args == 2: + # patch the object_info method which used to only take one argument + klass = sc.__class__ + klass._oinfo_orig = klass.object_info + klass.object_info = lambda s,x,y: s._oinfo_orig(x) + + #XXX: backwards compatibility for IPython < 1.0 + if not hasattr(kc, 'iopub_channel'): + kc.iopub_channel = kc.sub_channel + set_pid() + + if not connected: + echo("IPython connection attempt timed out", "Error") + return + else: + vim.command('redraw') + echo("IPython connection successful") # now that we're connect to an ipython kernel, activate completion # machinery, but do so only for the local buffer if the user added the @@ -207,7 +243,6 @@ def km_from_string(s=''): set bexpr=IPythonBalloonExpr() endif """) - set_pid() return km def echo(arg,style="Question"): From 56fa1d33a2cd1397d41cfe0e74a36d830333434b Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 4 Dec 2014 19:29:40 -0700 Subject: [PATCH 011/122] Change default options --- ftplugin/python/vim_ipython.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 97f6c61..2f44713 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -1,7 +1,7 @@ -reselect = False # reselect lines after sending from Visual mode -show_execution_count = True # wait to get numbers for In[43]: feedback? -monitor_subchannel = True # update vim-ipython 'shell' on every send? -run_flags= "-i" # flags to for IPython's run magic when using +reselect = False # reselect lines after sending from Visual mode +show_execution_count = False # wait to get numbers for In[43]: feedback? +monitor_subchannel = False # update vim-ipython 'shell' on every send? +run_flags= "-i" # flags to for IPython's run magic when using current_line = '' try: From 105fc88db08a8f8ae8040522a07ec999b52c6bf3 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 4 Dec 2014 19:30:08 -0700 Subject: [PATCH 012/122] Improve preview window behavior --- ftplugin/python/vim_ipython.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 2f44713..1c1fa1b 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -316,7 +316,7 @@ def get_doc_buffer(level=0): vim.command('nnoremap q :q') # Known issue: to enable the use of arrow keys inside the terminal when # viewing the documentation, comment out the next line - vim.command('nnoremap :q') + # vim.command('nnoremap :q') # and uncomment this line (which will work if you have a timoutlen set) #vim.command('nnoremap :q') b = vim.current.buffer @@ -330,8 +330,14 @@ def get_doc_buffer(level=0): #vim.command('pedit doc') #vim.command('normal! ') # go to previous window if level == 0: - # use the ReST formatting that ships with stock vim - vim.command('setlocal syntax=rst') + # highlight python code within rst + vim.command(r'unlet! b:current_syntax') + vim.command(r'syn include @rstPythonScript syntax/python.vim') + # 4 spaces + vim.command(r'syn region rstPythonRegion start=/^\v {4}/ end=/\v^( {4}|\n)@!/ contains=@rstPythonScript') + # >>> python code -> (doctests) + vim.command(r'syn region rstPythonRegion matchgroup=pythonDoctest start=/^>>>\s*/ end=/\n/ contains=@rstPythonScript') + vim.command(r'let b:current_syntax = "rst"') else: # use Python syntax highlighting vim.command('setlocal syntax=python') @@ -377,6 +383,9 @@ def update_subchannel_msgs(debug=False, force=False): msgs = kc.iopub_channel.get_msgs() b = vim.current.buffer startedin_vimipython = vim.eval('@%')=='vim-ipython' + nwindows = len(vim.windows) + currentwin = int(vim.eval('winnr()')) + previouswin = int(vim.eval('winnr("#")')) if not startedin_vimipython: # switch to preview window vim.command( @@ -461,7 +470,6 @@ def update_subchannel_msgs(debug=False, force=False): elif header == 'pyerr': c = m['content'] s = "\n".join(map(strip_color_escapes,c['traceback'])) - s += c['ename'] + ":" + c['evalue'] if s.find('\n') == -1: # somewhat ugly unicode workaround from @@ -481,8 +489,14 @@ def update_subchannel_msgs(debug=False, force=False): b.append(['']) if update_occured or force: vim.command('normal! G') # go to the end of the file - if not startedin_vimipython: - vim.command('normal! p') # go back to where you were + if len(vim.windows) > nwindows: + pwin = int(vim.current.window.number) + if pwin <= previouswin: + previouswin += 1 + if pwin <= currentwin: + currentwin += 1 + vim.command(str(previouswin) + 'wincmd w') + vim.command(str(currentwin) + 'wincmd w') return update_occured def get_child_msg(msg_id): From e6b42795bcd4c25335b41f0160d5de1e7b75e7ab Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 4 Dec 2014 19:30:40 -0700 Subject: [PATCH 013/122] Add default timeout to get_child_msg --- ftplugin/python/vim_ipython.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 1c1fa1b..dd39206 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -499,11 +499,11 @@ def update_subchannel_msgs(debug=False, force=False): vim.command(str(currentwin) + 'wincmd w') return update_occured -def get_child_msg(msg_id): +def get_child_msg(msg_id, timeout=1): # XXX: message handling should be split into its own process in the future while True: # get_msg will raise with Empty exception if no messages arrive in 1 second - m = kc.shell_channel.get_msg(timeout=1) + m = kc.shell_channel.get_msg(timeout=timeout) if m['parent_header']['msg_id'] == msg_id: break else: From a58a05939249d166c521ec9e22d58b52da2dea9f Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 4 Dec 2014 19:31:04 -0700 Subject: [PATCH 014/122] Don't put os in ipython namespace --- ftplugin/python/vim_ipython.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index dd39206..c92d98b 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -606,7 +606,7 @@ def set_pid(): Explicitly ask the ipython kernel for its pid """ global pid - lines = '\n'.join(['import os', '_pid = os.getpid()']) + lines = '\n'.join(['import os as _os', '_pid = _os.getpid()']) msg_id = send(lines, silent=True, user_variables=['_pid']) # wait to get message back from kernel From 0c557a11c60b2776f2e1cd89548dcbdfb35805b8 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 4 Dec 2014 19:31:18 -0700 Subject: [PATCH 015/122] Add run_ipy_input function --- ftplugin/python/vim_ipython.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index c92d98b..8240b62 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -539,7 +539,14 @@ def f_with_update(*args): @with_subchannel def run_this_file(): msg_id = send('%%run %s %s' % (run_flags, repr(vim.current.buffer.name),)) - print_prompt("In[]: %%run %s %s" % (run_flags, repr(vim.current.buffer.name)),msg_id) + print_prompt("%%run %s %s" % (run_flags, repr(vim.current.buffer.name)),msg_id) + +@with_subchannel +def run_ipy_input(): + lines = vim.eval('g:ipy_input') + msg_id = send(lines) + lines = unicode(lines, 'utf-8').replace('\n', u'\xac') + print_prompt(lines[:(int(vim.options['columns']) - 22)], msg_id) @with_subchannel def run_this_line(dedent=False): From df687120db1b1ad2fa280c1ef8ba61af968458a2 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 4 Dec 2014 19:31:36 -0700 Subject: [PATCH 016/122] Whitespace --- ftplugin/python/ipy.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index d7fd1cc..6d1570d 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -153,7 +153,7 @@ function! s:DoMappings() "map (IPython-PlotCloseAll) "pi custom - map (IPython-RunFile) + map (IPython-RunFile) " map x (IPython-RunLine) imap x (IPython-RunLine) map (IPython-RunLineAsTopLevel) From e6cd52d3daf80ab390f7138a8a1ba2f968cc27eb Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 7 Dec 2014 22:50:24 -0700 Subject: [PATCH 017/122] Handle findstart for non-index square bracket --- ftplugin/python/ipy.vim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 6d1570d..4a9bfd0 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -221,6 +221,10 @@ fun! CompleteIPython(findstart, base) while s:start > 0 && (line[s:start-1] =~ s:split_pattern \ || (g:ipython_greedy_matching && line[s:start-1] == '.' \ && s:start >= 2 && line[s:start-2] =~ '\k')) + if g:ipython_greedy_matching && line[s:start-1] == '[' && + \ (s:start == 1 || line[s:start-2] !~ '\k') + break + endif let s:start -= 1 endwhile python << endpython From 2ca1b0b0c0abe41848d7123788fd95472c594fa0 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Tue, 9 Dec 2014 20:36:55 -0700 Subject: [PATCH 018/122] Allow completion for nested dicts (adjacent ][) --- ftplugin/python/ipy.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 4a9bfd0..6c8a3d0 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -222,7 +222,7 @@ fun! CompleteIPython(findstart, base) \ || (g:ipython_greedy_matching && line[s:start-1] == '.' \ && s:start >= 2 && line[s:start-2] =~ '\k')) if g:ipython_greedy_matching && line[s:start-1] == '[' && - \ (s:start == 1 || line[s:start-2] !~ '\k') + \ (s:start == 1 || line[s:start-2] !~ '\k\|\]') break endif let s:start -= 1 From 82902b3d54baa964ca84921571fc8aed52dee6d8 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 14 Dec 2014 12:50:03 -0700 Subject: [PATCH 019/122] Do mappings on BufNewFile event --- ftplugin/python/ipy.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 6c8a3d0..79795cc 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -172,7 +172,7 @@ function! s:DoMappings() augroup vim_ipython_autostart autocmd! - autocmd BufEnter *.py,--Python-- if g:ipy_autostart && !exists('b:did_ipython') + autocmd BufEnter,BufNewFile *.py,--Python-- if g:ipy_autostart && !exists('b:did_ipython') \ | call s:DoMappings() | endif augroup END From 0c570c48b7fbfd38983ece2fe2b974c0633352be Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Tue, 16 Dec 2014 20:09:40 -0700 Subject: [PATCH 020/122] Fix error when completion contains \" --- ftplugin/python/ipy.vim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 79795cc..f8f6176 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -62,6 +62,8 @@ vim_ipython_path = vim.eval("expand(':h')") sys.path.append(vim_ipython_path) from vim_ipython import * +def clean(s): + return s.replace(r'\"', '"').replace('"', r'\"') EOF fun! toggle_send_on_save() @@ -272,9 +274,9 @@ except ValueError: for c, m in zip(completions, metadata): if 'CALLSIG' in m: split = m.partition('CALLSIG') - vim.command('call add(res, {"word": "'+c.replace('"', r'\"')+'", "menu": "'+split[0].replace('"', r'\"')+'", "info": "'+split[-1].replace('"', r'\"')+'"})') + vim.command('call add(res, {"word": "'+clean(c)+'", "menu": "'+clean(split[0])+'", "info": "'+clean(split[-1])+'"})') else: - vim.command('call add(res, {"word": "'+c.replace('"', r'\"')+'", "menu": "'+m.replace('"', r'\"')+'", "info": ""})') + vim.command('call add(res, {"word": "'+clean(c)+'", "menu": "'+clean(m)+'", "info": ""})') endpython "call extend(res,completions) return res From 275add78cbd338dec511c8ad0b44da47559560a5 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 25 Dec 2014 15:51:06 -0600 Subject: [PATCH 021/122] Change visual map to dedent lines --- ftplugin/python/ipy.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index f8f6176..f86ee56 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -159,8 +159,8 @@ function! s:DoMappings() " map x (IPython-RunLine) imap x (IPython-RunLine) map (IPython-RunLineAsTopLevel) - xmap x (IPython-RunLines) - xmap (IPython-RunLinesAsTopLevel) + xmap x (IPython-RunLinesAsTopLevel) + xmap (IPython-RunLines) map x (IPython-RunCell) " noremap I# From 6065ef046cfaa86b6956383690b54225358791c7 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 26 Dec 2014 13:04:29 -0600 Subject: [PATCH 022/122] Replace ugly vim command with pyevals --- ftplugin/python/ipy.vim | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index f86ee56..b5ac880 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -61,9 +61,6 @@ import sys vim_ipython_path = vim.eval("expand(':h')") sys.path.append(vim_ipython_path) from vim_ipython import * - -def clean(s): - return s.replace(r'\"', '"').replace('"', r'\"') EOF fun! toggle_send_on_save() @@ -274,9 +271,9 @@ except ValueError: for c, m in zip(completions, metadata): if 'CALLSIG' in m: split = m.partition('CALLSIG') - vim.command('call add(res, {"word": "'+clean(c)+'", "menu": "'+clean(split[0])+'", "info": "'+clean(split[-1])+'"})') + vim.command('call add(res, {"word": pyeval("c"), "menu": pyeval("split[0]"), "info": pyeval("split[-1]")})') else: - vim.command('call add(res, {"word": "'+clean(c)+'", "menu": "'+clean(m)+'", "info": ""})') + vim.command('call add(res, {"word": pyeval("c"), "menu": pyeval("m")})') endpython "call extend(res,completions) return res From 0a75c913535e20c104e3b8dcf5675d20461c207c Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Mon, 5 Jan 2015 23:26:38 -0700 Subject: [PATCH 023/122] Fix findstart for e.g. word['key'].word --- ftplugin/python/ipy.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index b5ac880..09fb35d 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -214,12 +214,14 @@ fun! CompleteIPython(findstart, base) let s:start = col('.') - 1 if line[s:start-1] !~ s:split_pattern && \ !(g:ipython_greedy_matching && s:start >= 2 - \ && line[s:start-2] =~ '\k') + \ && line[s:start-2] =~ '\k') && + \ line[s:start-2:s:start-1] !=# '].' return -1 endif while s:start > 0 && (line[s:start-1] =~ s:split_pattern \ || (g:ipython_greedy_matching && line[s:start-1] == '.' \ && s:start >= 2 && line[s:start-2] =~ '\k')) + \ || line[s:start-2:s:start-1] ==# '].' if g:ipython_greedy_matching && line[s:start-1] == '[' && \ (s:start == 1 || line[s:start-2] !~ '\k\|\]') break From 6bce3984ee819f4e0f7b37f7fede461923c14cdb Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 23 Jan 2015 14:38:35 -0700 Subject: [PATCH 024/122] Completion for imports --- ftplugin/python/ipy.vim | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 09fb35d..ba46723 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -58,6 +58,7 @@ endif python << EOF import vim import sys +import re vim_ipython_path = vim.eval("expand(':h')") sys.path.append(vim_ipython_path) from vim_ipython import * @@ -237,6 +238,12 @@ endpython let res = [] python << endpython base = vim.eval("a:base") +if re.match('^\s*import\s\w*', vim.current.line): + base = 'import ' + base +else: + match = re.match('^\s*from\s+\w+\s+import\s+', vim.current.line) + if match: + base = 'from ' + re.split(match.string, '\s+')[1] + ' import ' + base findstart = vim.eval("a:findstart") matches, metadata = ipy_complete(base, current_line, int(vim.eval('s:start')) + len(base)) # we need to be careful with unicode, because we can have unicode From ab0231aa0b3c767948dae01a457acb7c326d3329 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 23 Jan 2015 14:38:35 -0700 Subject: [PATCH 025/122] Completion for imports --- ftplugin/python/vim_ipython.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 8240b62..069c2fb 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -343,10 +343,15 @@ def get_doc_buffer(level=0): vim.command('setlocal syntax=python') def ipy_complete(base, current_line, pos): - msg_id = kc.shell_channel.complete( - base, - current_line[pos-len(base):pos], - len(base)) + if (re.match('^\s*(import|from)\s+', current_line) + or re.match('^\s*from\s+\w+\s+import\s+(\w+,\s+)*', current_line)): + msg_id = kc.shell_channel.complete( + base, current_line, pos) + else: + msg_id = kc.shell_channel.complete( + base, + current_line[pos-len(base):pos], + len(base)) try: m = get_child_msg(msg_id, timeout=2) matches = m['content']['matches'] From 180854883c861facc9a0ec8390d58cc6ca4ca8c6 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 23 Jan 2015 15:45:31 -0700 Subject: [PATCH 026/122] Improve completion of 'from module import a, ...' --- ftplugin/python/ipy.vim | 9 ++++++++- ftplugin/python/vim_ipython.py | 20 ++++++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index ba46723..613bd53 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -217,7 +217,14 @@ fun! CompleteIPython(findstart, base) \ !(g:ipython_greedy_matching && s:start >= 2 \ && line[s:start-2] =~ '\k') && \ line[s:start-2:s:start-1] !=# '].' - return -1 + if line =~# '\v^\s*from\s+\w+\s+import\s+(\w+,\s+)*' + python << endpython +current_line = vim.current.line +endpython + return col('.') - 1 + else + return -1 + endif endif while s:start > 0 && (line[s:start-1] =~ s:split_pattern \ || (g:ipython_greedy_matching && line[s:start-1] == '.' diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 069c2fb..b48caa3 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -343,15 +343,19 @@ def get_doc_buffer(level=0): vim.command('setlocal syntax=python') def ipy_complete(base, current_line, pos): - if (re.match('^\s*(import|from)\s+', current_line) - or re.match('^\s*from\s+\w+\s+import\s+(\w+,\s+)*', current_line)): - msg_id = kc.shell_channel.complete( - base, current_line, pos) + if re.match('^\s*(import|from)\s+', current_line): + pass else: - msg_id = kc.shell_channel.complete( - base, - current_line[pos-len(base):pos], - len(base)) + match = re.match('^\s*from\s+\w+\s+import\s+(\w+,\s+)*', current_line) + if match: + module = re.split(match.string.strip(), '\s+')[1] + current_line = 'from {module} import {base}'.format( + module=module, base=base) + pos = current_line.rindex(base) + else: + current_line = current_line[pos-len(base):pos] + pos = len(base) + msg_id = kc.shell_channel.complete(base, current_line, pos) try: m = get_child_msg(msg_id, timeout=2) matches = m['content']['matches'] From 668658b9fc29d6f06af10c4625054cd93a07906a Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sat, 24 Jan 2015 13:04:38 -0700 Subject: [PATCH 027/122] Don't map --- ftplugin/python/ipy.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 613bd53..b9fbe6f 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -133,7 +133,7 @@ function! s:DoMappings() if &buftype == '' map (IPython-RunFile) endif - map (IPython-RunLine) + " map (IPython-RunLine) map (IPython-RunLines) map ,d (IPython-OpenPyDoc) map (IPython-UpdateShell) From 4194ef18d05c17a58e37e842966815fe5d0b34ea Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Tue, 17 Feb 2015 23:06:27 -0700 Subject: [PATCH 028/122] Better skipped message handling --- ftplugin/python/vim_ipython.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index b48caa3..5a107cd 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -517,7 +517,9 @@ def get_child_msg(msg_id, timeout=1): break else: #got a message, but not the one we were looking for - echo('skipping a message on shell_channel','WarningMsg') + if m['msg_type'] != 'execute_reply': + echo('skipping a message on shell_channel (%s)' % m['msg_type'], + 'WarningMsg') return m def print_prompt(prompt,msg_id=None): From f3f8f218272582dc6e800d11f16ea9738c7e70e3 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sat, 7 Mar 2015 14:31:38 -0700 Subject: [PATCH 029/122] Fix findstart for some corner cases - Minus sign before the name of a dictionary, e.g. -data['foo - Period after dictionary when dictionary is first word on line, e.g. ^data['foo'].bar --- ftplugin/python/ipy.vim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index b9fbe6f..3fac60d 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -203,7 +203,7 @@ return l:doc endfunction if g:ipython_greedy_matching - let s:split_pattern = "[^= \r\n().]" + let s:split_pattern = "[^= \r\n().-]" else let s:split_pattern = '\k\|\.' endif @@ -228,8 +228,8 @@ endpython endif while s:start > 0 && (line[s:start-1] =~ s:split_pattern \ || (g:ipython_greedy_matching && line[s:start-1] == '.' - \ && s:start >= 2 && line[s:start-2] =~ '\k')) - \ || line[s:start-2:s:start-1] ==# '].' + \ && s:start >= 2 && line[s:start-2] =~ '\k') + \ || line[s:start-2:s:start-1] ==# '].') if g:ipython_greedy_matching && line[s:start-1] == '[' && \ (s:start == 1 || line[s:start-2] !~ '\k\|\]') break From 7f6d461f342f304437d179c33fc4ff7fd6265825 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sat, 7 Mar 2015 15:43:01 -0700 Subject: [PATCH 030/122] Change run_flags to a vim variable --- ftplugin/python/ipy.vim | 5 +++++ ftplugin/python/vim_ipython.py | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 3fac60d..adc41d6 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -36,6 +36,11 @@ if !exists('g:ipython_greedy_matching') let g:ipython_greedy_matching = 0 endif +" Use -i with %run magic by default +if !exists('g:ipython_run_flags') + let g:ipython_run_flags = '-i' +endif + " Automatically run :IPython in python files after running :IPython the first " time if !exists('g:ipy_autostart') diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 5a107cd..ce8360e 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -1,7 +1,6 @@ reselect = False # reselect lines after sending from Visual mode show_execution_count = False # wait to get numbers for In[43]: feedback? monitor_subchannel = False # update vim-ipython 'shell' on every send? -run_flags= "-i" # flags to for IPython's run magic when using current_line = '' try: @@ -549,8 +548,8 @@ def f_with_update(*args): @with_subchannel def run_this_file(): - msg_id = send('%%run %s %s' % (run_flags, repr(vim.current.buffer.name),)) - print_prompt("%%run %s %s" % (run_flags, repr(vim.current.buffer.name)),msg_id) + msg_id = send('%%run %s %s' % (vim.vars['ipython_run_flags'], repr(vim.current.buffer.name),)) + print_prompt("%%run %s %s" % (vim.vars['ipython_run_flags'], repr(vim.current.buffer.name)),msg_id) @with_subchannel def run_ipy_input(): From 801d2815291d51c0c80c9d5798018c2d65a78d14 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 8 Mar 2015 10:15:57 -0700 Subject: [PATCH 031/122] Add function to evaluate g:ipy_input into @@/@+/@- --- ftplugin/python/vim_ipython.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index ce8360e..a430095 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -641,6 +641,25 @@ def set_pid(): return pid +def eval_to_register(): + msg_id = send('', silent=True, + user_expressions={'expr': vim.vars['ipy_input']}) + try: + child = get_child_msg(msg_id) + except Empty: + echo("no reply from IPython kernel") + return + result = child['content']['user_expressions']['expr'] + try: + vim.command('call setreg(\'"\', "%s")' % + result['data']['text/plain'].replace('"', '\\"')) + except KeyError: + echo('{ename}: {evalue}'.format(**result)) + else: + vim.command('let @+ = @"') + vim.command('let @* = @"') + + def terminate_kernel_hack(): "Send SIGTERM to our the IPython kernel" import signal From 242c4c1f5cc2b37393ae4ac00765e59df703e290 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 8 Mar 2015 10:48:56 -0700 Subject: [PATCH 032/122] Fix import completion --- ftplugin/python/ipy.vim | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index adc41d6..9c8be89 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -250,13 +250,6 @@ endpython let res = [] python << endpython base = vim.eval("a:base") -if re.match('^\s*import\s\w*', vim.current.line): - base = 'import ' + base -else: - match = re.match('^\s*from\s+\w+\s+import\s+', vim.current.line) - if match: - base = 'from ' + re.split(match.string, '\s+')[1] + ' import ' + base -findstart = vim.eval("a:findstart") matches, metadata = ipy_complete(base, current_line, int(vim.eval('s:start')) + len(base)) # we need to be careful with unicode, because we can have unicode # completions for filenames (for the %run magic, for example). So the next From 88f6df855a12516778243575f54f0094b4000be3 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Mon, 16 Mar 2015 20:08:18 -0700 Subject: [PATCH 033/122] Do mappings on FileType python autocmd --- ftplugin/python/ipy.vim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 9c8be89..b226cd3 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -179,6 +179,8 @@ function! s:DoMappings() autocmd! autocmd BufEnter,BufNewFile *.py,--Python-- if g:ipy_autostart && !exists('b:did_ipython') \ | call s:DoMappings() | endif + autocmd FileType python if g:ipy_autostart && !exists('b:did_ipython') + \ | call s:DoMappings() | endif augroup END setlocal omnifunc=CompleteIPython From 80b3b5c2ff1ea23ef676f7b8f8dc75f70220b9ad Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 20 Mar 2015 14:52:46 -0700 Subject: [PATCH 034/122] Fix 'from .* import .*' when indent is non-zero --- ftplugin/python/vim_ipython.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index a430095..3313853 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -343,7 +343,8 @@ def get_doc_buffer(level=0): def ipy_complete(base, current_line, pos): if re.match('^\s*(import|from)\s+', current_line): - pass + pos -= len(current_line) - len(current_line.lstrip()) + current_line = current_line.lstrip() else: match = re.match('^\s*from\s+\w+\s+import\s+(\w+,\s+)*', current_line) if match: From 37df7f294cc4f01ce4f9950a0a753ca759689e1d Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sat, 28 Mar 2015 23:51:55 -0700 Subject: [PATCH 035/122] Fix findstart for decorators --- ftplugin/python/ipy.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index b226cd3..2c6e070 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -210,7 +210,7 @@ return l:doc endfunction if g:ipython_greedy_matching - let s:split_pattern = "[^= \r\n().-]" + let s:split_pattern = "[^= \r\n().-@]" else let s:split_pattern = '\k\|\.' endif From 8160fd3743132be3eb3e1c8eaf2e32fc7fcf06a6 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 29 Mar 2015 18:14:50 -0700 Subject: [PATCH 036/122] Change imap to K --- ftplugin/python/ipy.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 2c6e070..7fb4ca9 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -172,7 +172,7 @@ function! s:DoMappings() " xnoremap :s/^\([ \t]*\)#/\1/ nnoremap :IPythonInterrupt - inoremap :call GetDocBuffer() + inoremap K :call GetDocBuffer() endif augroup vim_ipython_autostart From c77185c206a7edb88aaab83170a070f90e097960 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Tue, 31 Mar 2015 17:53:09 -0700 Subject: [PATCH 037/122] Fix split_pattern --- ftplugin/python/ipy.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 7fb4ca9..73ac0d9 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -210,7 +210,7 @@ return l:doc endfunction if g:ipython_greedy_matching - let s:split_pattern = "[^= \r\n().-@]" + let s:split_pattern = "[^= \r\n().@-]" else let s:split_pattern = '\k\|\.' endif From f9592943634974957c4a5ceaab2d193180736178 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 26 Apr 2015 13:40:44 -0700 Subject: [PATCH 038/122] Change eval_to_register to eval_ipy_input --- ftplugin/python/vim_ipython.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 3313853..3e19ece 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -642,7 +642,7 @@ def set_pid(): return pid -def eval_to_register(): +def eval_ipy_input(var=None): msg_id = send('', silent=True, user_expressions={'expr': vim.vars['ipy_input']}) try: @@ -652,13 +652,18 @@ def eval_to_register(): return result = child['content']['user_expressions']['expr'] try: - vim.command('call setreg(\'"\', "%s")' % - result['data']['text/plain'].replace('"', '\\"')) + if var: + vim.command('let %s = "%s"' % ( + var, result['data']['text/plain'].replace('"', '\\"'))) + else: + vim.command('call setreg(\'"\', "%s")' % + result['data']['text/plain'].replace('"', '\\"')) except KeyError: echo('{ename}: {evalue}'.format(**result)) else: - vim.command('let @+ = @"') - vim.command('let @* = @"') + if not var: + vim.command('let @+ = @"') + vim.command('let @* = @"') def terminate_kernel_hack(): From ded53638dda265fd90a1ef37cfac42b6f37ef98d Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Mon, 18 May 2015 19:56:44 -0700 Subject: [PATCH 039/122] Allow completion of dict keys after *dictname[ --- ftplugin/python/ipy.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 73ac0d9..2c81609 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -210,7 +210,7 @@ return l:doc endfunction if g:ipython_greedy_matching - let s:split_pattern = "[^= \r\n().@-]" + let s:split_pattern = "[^= \r\n*().@-]" else let s:split_pattern = '\k\|\.' endif From 7af79b8177e75cbd75e84e2847f6cdfc8f9087af Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 5 Jul 2015 21:43:54 -0700 Subject: [PATCH 040/122] Improve ANSI code pattern --- ftplugin/python/vim_ipython.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index a51ae50..f2204b9 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -289,7 +289,7 @@ def get_doc(word, level=0): import re # from http://serverfault.com/questions/71285/in-centos-4-4-how-can-i-strip-escape-sequences-from-a-text-file -strip = re.compile('\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]') +strip = re.compile('\x1B\[([0-9]{1,2}(;[0-9]{1,2})*)?[mK]') def strip_color_escapes(s): return strip.sub('',s) From 69bef0015088c349a2c63f6994e3b0f1bfe33985 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 22 Jul 2015 21:00:28 -0700 Subject: [PATCH 041/122] Remove string prefix and quotes in eval_ipy_input --- ftplugin/python/vim_ipython.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index f2204b9..7d485ff 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -693,13 +693,18 @@ def eval_ipy_input(var=None): echo("no reply from IPython kernel") return result = child['content']['user_expressions']['expr'] + text = result['data']['text/plain'] try: if var: - vim.command('let %s = "%s"' % ( - var, result['data']['text/plain'].replace('"', '\\"'))) + from cStringIO import StringIO + from tokenize import STRING, generate_tokens + if generate_tokens(StringIO(text).readline).next()[0] == STRING: + from ast import parse + vim.vars[var.replace('g:', '')] = parse(text).body[0].value.s + else: + vim.command('let %s = "%s"' % (var, text.replace('"', '\\"'))) else: - vim.command('call setreg(\'"\', "%s")' % - result['data']['text/plain'].replace('"', '\\"')) + vim.command('call setreg(\'"\', "%s")' % text.replace('"', '\\"')) except KeyError: echo('{ename}: {evalue}'.format(**result)) else: From 342c6fdfbc9b94e2c471308c4d4ea145dc9c8c93 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 22 Jul 2015 21:59:58 -0700 Subject: [PATCH 042/122] Send _vim_client message after connecting --- ftplugin/python/vim_ipython.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 7d485ff..49fb506 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -230,6 +230,7 @@ def km_from_string(s=''): else: vim.command('redraw') echo("IPython connection successful") + send('"_vim_client";_=_;__=__', store_history=False) #XXX: backwards compatibility for IPython < 0.13 import inspect From cd48865862a5f7ec02c583fd9215a3975c90807f Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 29 Jul 2015 00:20:59 -0700 Subject: [PATCH 043/122] Set syntax correctly --- ftplugin/python/vim_ipython.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 49fb506..71fe63e 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -369,7 +369,7 @@ def get_doc_buffer(level=0): vim.command(r'syn region rstPythonRegion start=/^\v {4}/ end=/\v^( {4}|\n)@!/ contains=@rstPythonScript') # >>> python code -> (doctests) vim.command(r'syn region rstPythonRegion matchgroup=pythonDoctest start=/^>>>\s*/ end=/\n/ contains=@rstPythonScript') - vim.command(r'let b:current_syntax = "rst"') + vim.command(r'set syntax=rst') else: # use Python syntax highlighting vim.command('setlocal syntax=python') From f1be971bf7aafc9464144fe5cfe550e57122f232 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 2 Aug 2015 10:29:29 -0700 Subject: [PATCH 044/122] Fix exception handling and use float division --- ftplugin/python/vim_ipython.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 71fe63e..b50fe64 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -686,16 +686,17 @@ def set_pid(): def eval_ipy_input(var=None): - msg_id = send('', silent=True, - user_expressions={'expr': vim.vars['ipy_input']}) + msg_id = send('from __future__ import division; ' + '_expr = %s' % vim.vars['ipy_input'], silent=True, + user_expressions={'_expr': '_expr'}) try: child = get_child_msg(msg_id) except Empty: echo("no reply from IPython kernel") return - result = child['content']['user_expressions']['expr'] - text = result['data']['text/plain'] try: + result = child['content']['user_expressions']['_expr'] + text = result['data']['text/plain'] if var: from cStringIO import StringIO from tokenize import STRING, generate_tokens From 6e9d007cf9f48f490baa7d390eeb7cf24010105e Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Tue, 4 Aug 2015 22:33:52 -0700 Subject: [PATCH 045/122] Fix keys in wrong order and result undefined --- ftplugin/python/vim_ipython.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index b50fe64..9256f7d 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -694,9 +694,9 @@ def eval_ipy_input(var=None): except Empty: echo("no reply from IPython kernel") return + result = child['content']['user_expressions'] try: - result = child['content']['user_expressions']['_expr'] - text = result['data']['text/plain'] + text = result['_expr']['data']['text/plain'] if var: from cStringIO import StringIO from tokenize import STRING, generate_tokens @@ -708,7 +708,10 @@ def eval_ipy_input(var=None): else: vim.command('call setreg(\'"\', "%s")' % text.replace('"', '\\"')) except KeyError: - echo('{ename}: {evalue}'.format(**result)) + try: + echo('{ename}: {evalue}'.format(**child['content'])) + except Exception: + echo('Unknown error occurred') else: if not var: vim.command('let @+ = @"') From 300f507bd66ff053081a2a899c71b838766b41da Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 16 Aug 2015 10:55:07 -0700 Subject: [PATCH 046/122] Make get_doc_buffer work for g:ipy_input --- ftplugin/python/vim_ipython.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 9256f7d..c9f2c1f 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -331,11 +331,11 @@ def get_doc_msg(msg_id): b.extend(c.splitlines()) return b -def get_doc_buffer(level=0): +def get_doc_buffer(level=0, word=''): # empty string in case vim.eval return None vim.command("let isk_save = &isk") # save iskeyword list vim.command("let &isk = '@,48-57,_,192-255,.'") - word = vim.eval('expand("")') or '' + word = vim.eval('expand("")') or word vim.command("let &isk = isk_save") # restore iskeyword list doc = get_doc(word, level) if len(doc) ==0: @@ -591,6 +591,9 @@ def run_this_file(): @with_subchannel def run_ipy_input(): lines = vim.eval('g:ipy_input') + if lines.strip().endswith('?'): + return get_doc_buffer(level=1 if lines.strip().endswith('??') else 0, + word=lines.strip().rstrip('?')) msg_id = send(lines) lines = unicode(lines, 'utf-8').replace('\n', u'\xac') print_prompt(lines[:(int(vim.options['columns']) - 22)], msg_id) From 7ec7723cc8feb007b2ac93c9eb8f38a54a9debc6 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 16 Aug 2015 21:16:35 -0700 Subject: [PATCH 047/122] Disable visual map --- ftplugin/python/ipy.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 2c81609..761c4de 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -162,7 +162,7 @@ function! s:DoMappings() " map x (IPython-RunLine) imap x (IPython-RunLine) map (IPython-RunLineAsTopLevel) - xmap x (IPython-RunLinesAsTopLevel) + "xmap x (IPython-RunLinesAsTopLevel) xmap (IPython-RunLines) map x (IPython-RunCell) From f1a73ec0c1486370dcc3a1dce676e3bf66a0ef02 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 16 Aug 2015 21:17:32 -0700 Subject: [PATCH 048/122] Improve eval_ipy_input Don't use 'from __future__ import division' if input starts with %/!/$ Fix error printing for some cases --- ftplugin/python/vim_ipython.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index c9f2c1f..0e8a582 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -689,9 +689,13 @@ def set_pid(): def eval_ipy_input(var=None): - msg_id = send('from __future__ import division; ' - '_expr = %s' % vim.vars['ipy_input'], silent=True, - user_expressions={'_expr': '_expr'}) + if vim.vars['ipy_input'].startswith(('%', '!', '$')): + msg_id = send('', silent=True, + user_expressions={'_expr': vim.vars['ipy_input']}) + else: + msg_id = send('from __future__ import division; ' + '_expr = %s' % vim.vars['ipy_input'], silent=True, + user_expressions={'_expr': '_expr'}) try: child = get_child_msg(msg_id) except Empty: @@ -712,7 +716,10 @@ def eval_ipy_input(var=None): vim.command('call setreg(\'"\', "%s")' % text.replace('"', '\\"')) except KeyError: try: - echo('{ename}: {evalue}'.format(**child['content'])) + try: + echo('{ename}: {evalue}'.format(**child['content'])) + except KeyError: + echo('{ename}: {evalue}'.format(**result['_expr'])) except Exception: echo('Unknown error occurred') else: From b53d87ec2e94e7a38b8a1a1e4950fd7aacd43200 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 21 Aug 2015 12:30:47 -0700 Subject: [PATCH 049/122] Add IPythonHistory function --- ftplugin/python/ipy.vim | 21 +++++++++++++++++++++ ftplugin/python/vim_ipython.py | 10 ++++++++++ 2 files changed, 31 insertions(+) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 761c4de..7e691e9 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -295,3 +295,24 @@ endpython return res endif endfun + +function! IPythonHistory(pattern) + let res = [] + python << endpython +n = vim.vars.get('ipython_history_len', 100) +pattern = '*' + vim.eval('a:pattern') + '*' +if not len(pattern): + pattern = None +history = get_history(n, pattern=pattern) +seen = set() +for session, line, code in history: + if code.strip() not in seen: + seen.add(code.strip()) + code = code.encode(vim_encoding) + vim.command('call add(res, {' + '"session": +pyeval("session"), ' + '"line": +pyeval("line"), ' + '"code": pyeval("code")})') +endpython + return res +endfunction diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 0e8a582..9b56794 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -801,3 +801,13 @@ def toggle_reselect(): # #send('run -d %s' % (vim.current.buffer.name,)) # echo("In[]: run -d %s (using pdb)" % vim.current.buffer.name) +def get_history(n, pattern=None): + msg_id = kc.shell_channel.history( + hist_access_type='search' if pattern else 'tail', + pattern=pattern, n=n, unique=True) + try: + child = get_child_msg(msg_id) + return reversed(child['content']['history']) + except Empty: + echo("no reply from IPython kernel") + return [] From 17235158d93e3da5cffbcbe88a83e6eb17d024e9 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 21 Aug 2015 19:48:14 -0700 Subject: [PATCH 050/122] Fix calling get_doc_buffer with word argument --- ftplugin/python/vim_ipython.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 9b56794..f5f61a3 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -331,11 +331,11 @@ def get_doc_msg(msg_id): b.extend(c.splitlines()) return b -def get_doc_buffer(level=0, word=''): +def get_doc_buffer(level=0, word=None): # empty string in case vim.eval return None vim.command("let isk_save = &isk") # save iskeyword list vim.command("let &isk = '@,48-57,_,192-255,.'") - word = vim.eval('expand("")') or word + word = word or vim.eval('expand("")') vim.command("let &isk = isk_save") # restore iskeyword list doc = get_doc(word, level) if len(doc) ==0: @@ -689,6 +689,8 @@ def set_pid(): def eval_ipy_input(var=None): + if not vim.vars.get('ipy_input', None): + return if vim.vars['ipy_input'].startswith(('%', '!', '$')): msg_id = send('', silent=True, user_expressions={'_expr': vim.vars['ipy_input']}) From 376111ee3605c9d5c8db864e60bfc330ce377bc3 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 23 Aug 2015 13:53:17 -0700 Subject: [PATCH 051/122] Fix to include current session history --- ftplugin/python/vim_ipython.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index f5f61a3..f998996 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -807,9 +807,27 @@ def get_history(n, pattern=None): msg_id = kc.shell_channel.history( hist_access_type='search' if pattern else 'tail', pattern=pattern, n=n, unique=True) + results = [] try: child = get_child_msg(msg_id) - return reversed(child['content']['history']) + results.extend(child['content']['history']) except Empty: echo("no reply from IPython kernel") return [] + msg_id = send('', silent=True, user_expressions={ + '_hist': '[h for h in get_ipython().history_manager.get_range(' + 'get_ipython().history_manager.session_number)]', + }) + try: + child = get_child_msg(msg_id) + hist = child['content']['user_expressions']['_hist'] + from ast import literal_eval + from fnmatch import fnmatch + more = literal_eval(hist['data']['text/plain']) + results.extend(h for h in more if fnmatch(h[2], pattern or '*')) + except Empty: + echo("no reply from IPython kernel") + return reversed(results) + except KeyError: + pass + return reversed(results) From d9b1faa17ae23e9da3387dfa24f8b28bcc8f4c20 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 23 Aug 2015 13:53:44 -0700 Subject: [PATCH 052/122] Fall back to jedi#completions on timeout --- ftplugin/python/ipy.vim | 9 ++++++++- ftplugin/python/vim_ipython.py | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 7e691e9..45de906 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -252,7 +252,14 @@ endpython let res = [] python << endpython base = vim.eval("a:base") -matches, metadata = ipy_complete(base, current_line, int(vim.eval('s:start')) + len(base)) +try: + matches, metadata = ipy_complete(base, current_line, int(vim.eval('s:start')) + len(base)) +except IOError: + if vim.eval('exists("*jedi#completions")'): + vim.command('setlocal omnifunc=jedi#completions') + else: + vim.command('setlocal omnifunc=') + vim.command('return -1') # we need to be careful with unicode, because we can have unicode # completions for filenames (for the %run magic, for example). So the next # line will fail on those: diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index f998996..b313150 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -402,7 +402,7 @@ def ipy_complete(base, current_line, pos): return matches, metadata except Empty: echo("no reply from IPython kernel") - return [''], [''] + raise IOError def vim_ipython_is_open(): """ From 897dbc3352ecf1fb5508ab9afcc48b8dcc2bfe63 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 26 Aug 2015 20:14:20 -0700 Subject: [PATCH 053/122] Add ipython_history_timeout option --- ftplugin/python/vim_ipython.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index b313150..e568817 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -809,7 +809,8 @@ def get_history(n, pattern=None): pattern=pattern, n=n, unique=True) results = [] try: - child = get_child_msg(msg_id) + child = get_child_msg( + msg_id, timeout=float(vim.vars.get('ipython_history_timeout', 2))) results.extend(child['content']['history']) except Empty: echo("no reply from IPython kernel") @@ -819,7 +820,8 @@ def get_history(n, pattern=None): 'get_ipython().history_manager.session_number)]', }) try: - child = get_child_msg(msg_id) + child = get_child_msg( + msg_id, timeout=float(vim.vars.get('ipython_history_timeout', 2))) hist = child['content']['user_expressions']['_hist'] from ast import literal_eval from fnmatch import fnmatch From 2052ce41eadb9a4150339b588d32886ec57fdaf4 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 26 Aug 2015 20:14:40 -0700 Subject: [PATCH 054/122] Fix check for empty pattern --- ftplugin/python/ipy.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 45de906..7cf9059 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -308,7 +308,7 @@ function! IPythonHistory(pattern) python << endpython n = vim.vars.get('ipython_history_len', 100) pattern = '*' + vim.eval('a:pattern') + '*' -if not len(pattern): +if pattern == '**': pattern = None history = get_history(n, pattern=pattern) seen = set() From 57725d113ae1cc152cf2345b0171b60552581bc4 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 30 Aug 2015 17:56:39 -0700 Subject: [PATCH 055/122] Add maps to change filetype in doc buffer --- ftplugin/python/vim_ipython.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index e568817..a062852 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -346,6 +346,10 @@ def get_doc_buffer(level=0, word=None): vim.command('setlocal modifiable noro') # doc window quick quit keys: 'q' and 'escape' vim.command('nnoremap q :q') + # shortcuts to change filetype/syntax + vim.command('nnoremap m :setfiletype man') + vim.command('nnoremap p :setfiletype python') + vim.command('nnoremap r :setfiletype rst') # Known issue: to enable the use of arrow keys inside the terminal when # viewing the documentation, comment out the next line # vim.command('nnoremap :q') From 2e465284ee567cbc74ea809d69a3040803e95270 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Mon, 7 Sep 2015 12:13:15 -0700 Subject: [PATCH 056/122] Disable insert mode map --- ftplugin/python/ipy.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 7cf9059..b74bb9f 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -160,7 +160,7 @@ function! s:DoMappings() "pi custom map (IPython-RunFile) " map x (IPython-RunLine) - imap x (IPython-RunLine) + " imap x (IPython-RunLine) map (IPython-RunLineAsTopLevel) "xmap x (IPython-RunLinesAsTopLevel) xmap (IPython-RunLines) From 611167b5f51a5b4a7e6dc50aa7575cfcf2c56c8a Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Mon, 7 Sep 2015 12:13:32 -0700 Subject: [PATCH 057/122] Add option to not store history in run_ipy_input --- ftplugin/python/vim_ipython.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index a062852..868bed6 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -598,7 +598,7 @@ def run_ipy_input(): if lines.strip().endswith('?'): return get_doc_buffer(level=1 if lines.strip().endswith('??') else 0, word=lines.strip().rstrip('?')) - msg_id = send(lines) + msg_id = send(lines, store_history=vim.vars.get('ipython_store_history', True)) lines = unicode(lines, 'utf-8').replace('\n', u'\xac') print_prompt(lines[:(int(vim.options['columns']) - 22)], msg_id) From 6fe7198dc151b6e3634999a9824b76c45431f18f Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sat, 19 Sep 2015 12:38:46 -0700 Subject: [PATCH 058/122] Use run_cython for Cython files --- ftplugin/python/vim_ipython.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 868bed6..2ee0c6d 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -17,6 +17,7 @@ def __getattribute__(self, key): vim = NoOp() print("uh oh, not running inside vim") +import os import sys import time @@ -589,8 +590,17 @@ def f_with_update(*args): @with_subchannel def run_this_file(): - msg_id = send('%%run %s %s' % (vim.vars['ipython_run_flags'], repr(vim.current.buffer.name),)) - print_prompt("%%run %s %s" % (vim.vars['ipython_run_flags'], repr(vim.current.buffer.name)),msg_id) + ext = os.path.splitext(vim.current.buffer.name)[-1][1:] + if ext in ('pxd', 'pxi', 'pyx', 'pyxbld'): + cmd = ' '.join(filter(None, ( + '%run_cython', + vim.vars.get('cython_run_flags', ''), + repr(vim.current.buffer.name)))) + else: + cmd = '%%run %s %s' % (vim.vars['ipython_run_flags'], + repr(vim.current.buffer.name)) + msg_id = send(cmd) + print_prompt(cmd, msg_id) @with_subchannel def run_ipy_input(): From 5a8ab4e45b151c30df55a9b413cc19c41575c4da Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 25 Sep 2015 18:05:51 -0700 Subject: [PATCH 059/122] Remove neocomplete source --- .../neocomplete/sources/ipythoncomplete.vim | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 autoload/neocomplete/sources/ipythoncomplete.vim diff --git a/autoload/neocomplete/sources/ipythoncomplete.vim b/autoload/neocomplete/sources/ipythoncomplete.vim deleted file mode 100644 index 6253f01..0000000 --- a/autoload/neocomplete/sources/ipythoncomplete.vim +++ /dev/null @@ -1,31 +0,0 @@ -if !has('python') - finish -endif - -let s:save_cpo = &cpo -set cpo&vim - -let s:source = { - \ 'name' : 'ipython-complete', - \ 'kind' : 'keyword', - \ 'mark' : '[IPy]', - \ 'rank' : 4, - \ } - -function! neocomplete#sources#ipythoncomplete#complete(findstart, base) - if &filetype == 'python' - silent! return CompleteIPython(a:findstart, a:base) - endif -endfunction - -function! s:source.gather_candidates(context) - return neocomplete#sources#ipythoncomplete#complete(0, '') -endfunction - -function! neocomplete#sources#ipythoncomplete#define() - return s:source -endfunction - -let &cpo = s:save_cpo -unlet s:save_cpo - From 37f8a9c7992b0b2eebb5f5afaef07587bd8f57be Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Tue, 6 Oct 2015 21:37:20 -0700 Subject: [PATCH 060/122] Add completion timeout and history by session --- ftplugin/python/ipy.vim | 11 ++++++++--- ftplugin/python/vim_ipython.py | 21 ++++++++++++++------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index b74bb9f..7c00156 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -303,16 +303,21 @@ endpython endif endfun -function! IPythonHistory(pattern) +function! IPythonHistory(pattern, ...) + let session = a:0 > 0 ? a:1 : (-1) let res = [] python << endpython n = vim.vars.get('ipython_history_len', 100) pattern = '*' + vim.eval('a:pattern') + '*' if pattern == '**': pattern = None -history = get_history(n, pattern=pattern) +if int(vim.eval('session')) >= 0: + history = get_session_history(session=int(vim.eval('session')), + pattern=pattern) +else: + history = get_history(n, pattern=pattern) seen = set() -for session, line, code in history: +for session, line, code in reversed(history): if code.strip() not in seen: seen.add(code.strip()) code = code.encode(vim_encoding) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 2ee0c6d..b5d089e 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -395,7 +395,7 @@ def ipy_complete(base, current_line, pos): pos = len(base) msg_id = kc.shell_channel.complete(base, current_line, pos) try: - m = get_child_msg(msg_id, timeout=2) + m = get_child_msg(msg_id, timeout=vim.vars.get('ipython_completion_timeout', 2)) matches = m['content']['matches'] metadata = m['content']['metadata'] # we need to be careful with unicode, because we can have unicode @@ -820,7 +820,8 @@ def toggle_reselect(): def get_history(n, pattern=None): msg_id = kc.shell_channel.history( hist_access_type='search' if pattern else 'tail', - pattern=pattern, n=n, unique=True) + pattern=pattern, n=n, unique=True, + raw=vim.vars.get('ipython_history_raw', True)) results = [] try: child = get_child_msg( @@ -829,9 +830,16 @@ def get_history(n, pattern=None): except Empty: echo("no reply from IPython kernel") return [] + results.extend(get_session_history(pattern=pattern)) + return results + +def get_session_history(session=None, pattern=None): msg_id = send('', silent=True, user_expressions={ '_hist': '[h for h in get_ipython().history_manager.get_range(' - 'get_ipython().history_manager.session_number)]', + '%s, raw=%s)]' + % (str(session) if session else + 'get_ipython().history_manager.session_number', + vim.vars.get('ipython_history_raw', 'True')), }) try: child = get_child_msg( @@ -840,10 +848,9 @@ def get_history(n, pattern=None): from ast import literal_eval from fnmatch import fnmatch more = literal_eval(hist['data']['text/plain']) - results.extend(h for h in more if fnmatch(h[2], pattern or '*')) + return [h for h in more if fnmatch(h[2], pattern or '*')] except Empty: echo("no reply from IPython kernel") - return reversed(results) + return [] except KeyError: - pass - return reversed(results) + return [] From d4c78ab79b73197ecbd27fc6884082cd127cf0f0 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 11 Oct 2015 11:51:58 -0700 Subject: [PATCH 061/122] IPython 3+ compatibility --- ftplugin/python/vim_ipython.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index b5d089e..edea30d 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -199,7 +199,7 @@ def km_from_string(s=''): # < 3.0 send = kc.shell_channel.execute - kc.shell_channel.execute('', silent=True) + send('', silent=True) try: msg = kc.shell_channel.get_msg(timeout=1) connected = True From 2343f9a446a21fa7c2729a780ec63062abe77ef1 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 11 Oct 2015 18:47:11 -0700 Subject: [PATCH 062/122] Py3 compat and don't show 0 for session number --- ftplugin/python/vim_ipython.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index edea30d..91a678b 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -124,7 +124,7 @@ def km_from_string(s=''): # < 0.12, no find_connection_file pass - global km, kc, send + global km, kc, send, history # Test if connection is still alive connected = False @@ -195,9 +195,11 @@ def km_from_string(s=''): try: send = kc.execute + history = kc.history except AttributeError: # < 3.0 send = kc.shell_channel.execute + history = kc.shell_channel.history send('', silent=True) try: @@ -818,7 +820,7 @@ def toggle_reselect(): # echo("In[]: run -d %s (using pdb)" % vim.current.buffer.name) def get_history(n, pattern=None): - msg_id = kc.shell_channel.history( + msg_id = history( hist_access_type='search' if pattern else 'tail', pattern=pattern, n=n, unique=True, raw=vim.vars.get('ipython_history_raw', True)) @@ -840,15 +842,19 @@ def get_session_history(session=None, pattern=None): % (str(session) if session else 'get_ipython().history_manager.session_number', vim.vars.get('ipython_history_raw', 'True')), + '_session': 'get_ipython().history_manager.session_number', }) try: child = get_child_msg( msg_id, timeout=float(vim.vars.get('ipython_history_timeout', 2))) hist = child['content']['user_expressions']['_hist'] + session = child['content']['user_expressions']['_session'] + session = int(session['data']['text/plain']) from ast import literal_eval from fnmatch import fnmatch more = literal_eval(hist['data']['text/plain']) - return [h for h in more if fnmatch(h[2], pattern or '*')] + return [(s if s > 0 else session, l, c) for s, l, c in more + if fnmatch(c, pattern or '*')] except Empty: echo("no reply from IPython kernel") return [] From a0dc32aa87e11dbb3370d07b618c69c343aae3d3 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Mon, 12 Oct 2015 09:58:55 -0700 Subject: [PATCH 063/122] Fix encoding issues --- ftplugin/python/ipy.vim | 1 - ftplugin/python/vim_ipython.py | 11 ++++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 7c00156..7669c62 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -320,7 +320,6 @@ seen = set() for session, line, code in reversed(history): if code.strip() not in seen: seen.add(code.strip()) - code = code.encode(vim_encoding) vim.command('call add(res, {' '"session": +pyeval("session"), ' '"line": +pyeval("line"), ' diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 91a678b..ec1419c 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -725,7 +725,8 @@ def eval_ipy_input(var=None): if var: from cStringIO import StringIO from tokenize import STRING, generate_tokens - if generate_tokens(StringIO(text).readline).next()[0] == STRING: + if generate_tokens(StringIO( + text.encode(vim_encoding)).readline).next()[0] == STRING: from ast import parse vim.vars[var.replace('g:', '')] = parse(text).body[0].value.s else: @@ -824,11 +825,11 @@ def get_history(n, pattern=None): hist_access_type='search' if pattern else 'tail', pattern=pattern, n=n, unique=True, raw=vim.vars.get('ipython_history_raw', True)) - results = [] try: child = get_child_msg( msg_id, timeout=float(vim.vars.get('ipython_history_timeout', 2))) - results.extend(child['content']['history']) + results = [(s, l, c.encode(vim_encoding)) + for s, l, c in child['content']['history']] except Empty: echo("no reply from IPython kernel") return [] @@ -849,10 +850,10 @@ def get_session_history(session=None, pattern=None): msg_id, timeout=float(vim.vars.get('ipython_history_timeout', 2))) hist = child['content']['user_expressions']['_hist'] session = child['content']['user_expressions']['_session'] - session = int(session['data']['text/plain']) + session = int(session['data']['text/plain'].encode(vim_encoding)) from ast import literal_eval from fnmatch import fnmatch - more = literal_eval(hist['data']['text/plain']) + more = literal_eval(hist['data']['text/plain'].encode(vim_encoding)) return [(s if s > 0 else session, l, c) for s, l, c in more if fnmatch(c, pattern or '*')] except Empty: From f33d69fc1850c699e93b8a7d1e1b962bae79c1b8 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Tue, 27 Oct 2015 21:22:44 -0700 Subject: [PATCH 064/122] Improve handling of argument to km_from_string --- ftplugin/python/vim_ipython.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index ec1419c..8a500b4 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -130,14 +130,18 @@ def km_from_string(s=''): connected = False starttime = time.time() attempt = 0 + s = s.replace('--existing', '') while not connected and (time.time() - starttime) < 5.0: + if not attempt and os.path.isfile(s): + fullpath = s + else: + try: + s = fullpath = find_connection_file('kernel*') + except IOError: + echo("IPython connection attempt #%d failed - no kernel file" % attempt, "Warning") + time.sleep(1) + continue attempt += 1 - try: - fullpath = find_connection_file('kernel*') - except IOError: - echo("IPython connection attempt #%d failed - no kernel file" % attempt, "Warning") - time.sleep(1) - continue km = KernelManager(connection_file=fullpath) km.load_connection_file() @@ -145,7 +149,6 @@ def km_from_string(s=''): kc = km.client() kc.start_channels() - s = s.replace('--existing', '') if 'connection_file' in KernelManager.class_trait_names(): # 0.12 uses files instead of a collection of ports # include default IPython search path From d5fe517f9fde835363cd51096cf38266c5273eb6 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 28 Oct 2015 23:29:35 -0700 Subject: [PATCH 065/122] Only set unique if pattern is non-empty --- ftplugin/python/ipy.vim | 5 +++-- ftplugin/python/vim_ipython.py | 11 ++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 7669c62..7567b3d 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -308,6 +308,7 @@ function! IPythonHistory(pattern, ...) let res = [] python << endpython n = vim.vars.get('ipython_history_len', 100) +unique = vim.eval('a:pattern') != '' pattern = '*' + vim.eval('a:pattern') + '*' if pattern == '**': pattern = None @@ -315,10 +316,10 @@ if int(vim.eval('session')) >= 0: history = get_session_history(session=int(vim.eval('session')), pattern=pattern) else: - history = get_history(n, pattern=pattern) + history = get_history(n, pattern=pattern, unique=unique) seen = set() for session, line, code in reversed(history): - if code.strip() not in seen: + if not unique or code.strip() not in seen: seen.add(code.strip()) vim.command('call add(res, {' '"session": +pyeval("session"), ' diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 8a500b4..9650dea 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -823,20 +823,21 @@ def toggle_reselect(): # #send('run -d %s' % (vim.current.buffer.name,)) # echo("In[]: run -d %s (using pdb)" % vim.current.buffer.name) -def get_history(n, pattern=None): +def get_history(n, pattern=None, unique=True): msg_id = history( hist_access_type='search' if pattern else 'tail', - pattern=pattern, n=n, unique=True, + pattern=pattern, n=n, unique=unique, raw=vim.vars.get('ipython_history_raw', True)) try: child = get_child_msg( msg_id, timeout=float(vim.vars.get('ipython_history_timeout', 2))) - results = [(s, l, c.encode(vim_encoding)) - for s, l, c in child['content']['history']] + results = [(session, line, code.encode(vim_encoding)) + for session, line, code in child['content']['history']] except Empty: echo("no reply from IPython kernel") return [] - results.extend(get_session_history(pattern=pattern)) + if unique: + results.extend(get_session_history(pattern=pattern)) return results def get_session_history(session=None, pattern=None): From 85f6a3db3032497f2feb98ac13ada3f0233179a6 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 30 Oct 2015 13:07:21 -0700 Subject: [PATCH 066/122] Fix completion of 'from a.b import ...' --- ftplugin/python/vim_ipython.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 9650dea..13d21f2 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -389,9 +389,9 @@ def ipy_complete(base, current_line, pos): pos -= len(current_line) - len(current_line.lstrip()) current_line = current_line.lstrip() else: - match = re.match('^\s*from\s+\w+\s+import\s+(\w+,\s+)*', current_line) + match = re.match('^\s*from\s+\w+(\.\w+)*\s+import\s+(\w+,\s+)*', current_line) if match: - module = re.split(match.string.strip(), '\s+')[1] + module = match.string.strip().split()[1] current_line = 'from {module} import {base}'.format( module=module, base=base) pos = current_line.rindex(base) From b505eb479899849f7b36a0cbe435f4dbca9200ab Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 30 Oct 2015 18:38:00 -0700 Subject: [PATCH 067/122] Allow anchoring start/end of pattern --- ftplugin/python/ipy.vim | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 7567b3d..03cc9a2 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -308,10 +308,13 @@ function! IPythonHistory(pattern, ...) let res = [] python << endpython n = vim.vars.get('ipython_history_len', 100) -unique = vim.eval('a:pattern') != '' -pattern = '*' + vim.eval('a:pattern') + '*' -if pattern == '**': +pattern = vim.eval('a:pattern') +if pattern: + if not pattern.startswith('*') and not pattern.endswith('*'): + pattern = '*{0}*'.format(pattern) +else: pattern = None +unique = pattern is not None if int(vim.eval('session')) >= 0: history = get_session_history(session=int(vim.eval('session')), pattern=pattern) From 881a0caf99a1398be6cb2d92d735c4bdec5c3ce5 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 1 Nov 2015 12:44:01 -0700 Subject: [PATCH 068/122] Handle unicode characters in completion --- ftplugin/python/ipy.vim | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 03cc9a2..d32578d 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -218,12 +218,12 @@ endif fun! CompleteIPython(findstart, base) if a:findstart " locate the start of the word - let line = getline('.')[:col('.')-1] - let s:start = col('.') - 1 + let line = split(getline('.')[:col('.')-1], '\zs') + let s:start = strchars(getline('.')[:col('.')-1]) - 1 if line[s:start-1] !~ s:split_pattern && \ !(g:ipython_greedy_matching && s:start >= 2 \ && line[s:start-2] =~ '\k') && - \ line[s:start-2:s:start-1] !=# '].' + \ join(line[s:start-2:s:start-1], '') !=# '].' if line =~# '\v^\s*from\s+\w+\s+import\s+(\w+,\s+)*' python << endpython current_line = vim.current.line @@ -236,7 +236,7 @@ endpython while s:start > 0 && (line[s:start-1] =~ s:split_pattern \ || (g:ipython_greedy_matching && line[s:start-1] == '.' \ && s:start >= 2 && line[s:start-2] =~ '\k') - \ || line[s:start-2:s:start-1] ==# '].') + \ || join(line[s:start-2:s:start-1], '') ==# '].') if g:ipython_greedy_matching && line[s:start-1] == '[' && \ (s:start == 1 || line[s:start-2] !~ '\k\|\]') break @@ -246,7 +246,8 @@ endpython python << endpython current_line = vim.current.line endpython - return s:start + return s:start + len(join(line[: s:start], '')) + \ - len(getline('.')[: s:start]) else " find months matching with "a:base" let res = [] From 20a34baafcd90d3f5d231c220d4b263424bf6214 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 1 Nov 2015 12:54:36 -0700 Subject: [PATCH 069/122] Support Python 3 and IPython 3/4 --- ftplugin/python/ipy.vim | 109 ++++++++++++++---------- ftplugin/python/vim_ipython.py | 149 +++++++++++++++++++-------------- 2 files changed, 149 insertions(+), 109 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index d32578d..49b24bc 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -18,12 +18,26 @@ " " written by Paul Ivanov (http://pirsquared.org) " -if !has('python') +if !(has('python') || has('python3')) " exit if python is not available. " XXX: raise an error message here finish endif +if has('python3') && get(g:, 'pymode_python', '') !=# 'python' + command! -nargs=1 Python2or3 python3 + Python2or3 PY3 = True + function! IPythonPyeval(arg) + return py3eval(a:arg) + endfunction +else + command! -nargs=1 Python2or3 python + Python2or3 PY3 = False + function! IPythonPyeval(arg) + return pyeval(a:arg) + endfunction +endif + " Allow custom mappings. if !exists('g:ipy_perform_mappings') let g:ipy_perform_mappings = 1 @@ -60,19 +74,19 @@ if !exists('g:ipy_completefunc') let g:ipy_completefunc = 'omni' endif -python << EOF +Python2or3 << endpython import vim import sys import re vim_ipython_path = vim.eval("expand(':h')") sys.path.append(vim_ipython_path) from vim_ipython import * -EOF +endpython fun! toggle_send_on_save() if exists("s:ssos") && s:ssos == 0 let s:ssos = 1 - au BufWritePost *.py :py run_this_file() + au BufWritePost *.py :Python2or3 run_this_file() echo "Autosend On" else let s:ssos = 0 @@ -99,38 +113,38 @@ augroup vim-ipython " buffer we may have opened up doesn't get closed just because of an idle " event (i.e. user pressed \d and then left the buffer that popped up, but " expects it to stay there). - au CursorHold *.*,vim-ipython :python if update_subchannel_msgs(): echo("vim-ipython shell updated (on idle)",'Operator') + au CursorHold *.*,vim-ipython :Python2or3 if update_subchannel_msgs(): echo("vim-ipython shell updated (on idle)",'Operator') " XXX: broken - cursor hold update for insert mode moves the cursor one " character to the left of the last character (update_subchannel_msgs must be " doing this) - "au CursorHoldI *.* :python if update_subchannel_msgs(): echo("vim-ipython shell updated (on idle)",'Operator') + "au CursorHoldI *.* :Python2or3 if update_subchannel_msgs(): echo("vim-ipython shell updated (on idle)",'Operator') " Same as above, but on regaining window focus (mostly for GUIs) - au FocusGained *.*,vim-ipython :python if update_subchannel_msgs(): echo("vim-ipython shell updated (on input focus)",'Operator') + au FocusGained *.*,vim-ipython :Python2or3 if update_subchannel_msgs(): echo("vim-ipython shell updated (on input focus)",'Operator') " Update vim-ipython buffer when we move the cursor there. A message is only " displayed if vim-ipython buffer has been updated. - au BufEnter vim-ipython :python if update_subchannel_msgs(): echo("vim-ipython shell updated (on buffer enter)",'Operator') + au BufEnter vim-ipython :Python2or3 if update_subchannel_msgs(): echo("vim-ipython shell updated (on buffer enter)",'Operator') augroup END " Setup plugin mappings for the most common ways to interact with ipython. -noremap (IPython-RunFile) :update:python run_this_file() -noremap (IPython-RunLine) :python run_this_line() -noremap (IPython-RunLines) :python run_these_lines() -noremap (IPython-OpenPyDoc) :python get_doc_buffer() -noremap (IPython-UpdateShell) :python if update_subchannel_msgs(force=True): echo("vim-ipython shell updated",'Operator') -noremap (IPython-ToggleReselect) :python toggle_reselect() -"noremap (IPython-StartDebugging) :python send('%pdb') -"noremap (IPython-BreakpointSet) :python set_breakpoint() -"noremap (IPython-BreakpointClear) :python clear_breakpoint() -"noremap (IPython-DebugThisFile) :python run_this_file_pdb() -"noremap (IPython-BreakpointClearAll) :python clear_all_breaks() +noremap (IPython-RunFile) :update:Python2or3 run_this_file() +noremap (IPython-RunLine) :Python2or3 run_this_line() +noremap (IPython-RunLines) :Python2or3 run_these_lines() +noremap (IPython-OpenPyDoc) :Python2or3 get_doc_buffer() +noremap (IPython-UpdateShell) :Python2or3 if update_subchannel_msgs(force=True): echo("vim-ipython shell updated",'Operator') +noremap (IPython-ToggleReselect) :Python2or3 toggle_reselect() +"noremap (IPython-StartDebugging) :Python2or3 send('%pdb') +"noremap (IPython-BreakpointSet) :Python2or3 set_breakpoint() +"noremap (IPython-BreakpointClear) :Python2or3 clear_breakpoint() +"noremap (IPython-DebugThisFile) :Python2or3 run_this_file_pdb() +"noremap (IPython-BreakpointClearAll) :Python2or3 clear_all_breaks() noremap (IPython-ToggleSendOnSave) :call toggle_send_on_save() -noremap (IPython-PlotClearCurrent) :python run_command("plt.clf()") -noremap (IPython-PlotCloseAll) :python run_command("plt.close('all')") -noremap (IPython-RunLineAsTopLevel) :python dedent_run_this_line() -xnoremap (IPython-RunLinesAsTopLevel) :python dedent_run_these_lines() +noremap (IPython-PlotClearCurrent) :Python2or3 run_command("plt.clf()") +noremap (IPython-PlotCloseAll) :Python2or3 run_command("plt.close('all')") +noremap (IPython-RunLineAsTopLevel) :Python2or3 dedent_run_this_line() +xnoremap (IPython-RunLinesAsTopLevel) :Python2or3 dedent_run_these_lines() function! s:DoMappings() let b:did_ipython = 1 @@ -193,15 +207,15 @@ function! s:GetDocBuffer() nnoremap ` p:if winheight(0)<30res 30endifundojoinstartinsert! endfunction -command! -nargs=* IPython :call DoMappings()|:py km_from_string("") -command! -nargs=0 IPythonClipboard :py km_from_string(vim.eval('@+')) -command! -nargs=0 IPythonXSelection :py km_from_string(vim.eval('@*')) -command! -nargs=* IPythonNew :py new_ipy("") -command! -nargs=* IPythonInterrupt :py interrupt_kernel_hack("") -command! -nargs=0 IPythonTerminate :py terminate_kernel_hack() +command! -nargs=* IPython :call DoMappings()|:Python2or3 km_from_string("") +command! -nargs=0 IPythonClipboard :Python2or3 km_from_string(vim.eval('@+')) +command! -nargs=0 IPythonXSelection :Python2or3 km_from_string(vim.eval('@*')) +command! -nargs=* IPythonNew :Python2or3 new_ipy("") +command! -nargs=* IPythonInterrupt :Python2or3 interrupt_kernel_hack("") +command! -nargs=0 IPythonTerminate :Python2or3 terminate_kernel_hack() function! IPythonBalloonExpr() -python << endpython +Python2or3 << endpython word = vim.eval('v:beval_text') reply = get_doc(word) vim.command("let l:doc = %s"% reply) @@ -225,7 +239,7 @@ fun! CompleteIPython(findstart, base) \ && line[s:start-2] =~ '\k') && \ join(line[s:start-2:s:start-1], '') !=# '].' if line =~# '\v^\s*from\s+\w+\s+import\s+(\w+,\s+)*' - python << endpython + Python2or3 << endpython current_line = vim.current.line endpython return col('.') - 1 @@ -243,7 +257,7 @@ endpython endif let s:start -= 1 endwhile - python << endpython + Python2or3 << endpython current_line = vim.current.line endpython return s:start + len(join(line[: s:start], '')) @@ -251,10 +265,11 @@ endpython else " find months matching with "a:base" let res = [] - python << endpython + let start = s:start + Python2or3 << endpython base = vim.eval("a:base") try: - matches, metadata = ipy_complete(base, current_line, int(vim.eval('s:start')) + len(base)) + matches, metadata = ipy_complete(base, current_line, int(vim.eval('start')) + len(base)) except IOError: if vim.eval('exists("*jedi#completions")'): vim.command('setlocal omnifunc=jedi#completions') @@ -267,15 +282,18 @@ except IOError: #completions= [str(u) for u in matches] # because str() won't work for non-ascii characters # and we also have problems with unicode in vim, hence the following: -completions = [s.encode(vim_encoding) for s in matches] -metadata = [s.encode(vim_encoding) for s in metadata] +if PY3: + completions = matches +else: + completions = [s.encode(vim_encoding) for s in matches] + metadata = [s.encode(vim_encoding) for s in metadata] if vim.vars['ipython_dictionary_completion'] and not vim.vars['ipython_greedy_matching']: for c in completions: if c.endswith("']"): - completions = filter(lambda c: c.endswith("']"), completions) + completions = [c for c in completions if c.endswith("']")] break elif c.endswith('"]'): - completions = filter(lambda c: c.endswith('"]'), completions) + completions = [c for c in completions if c.endswith('"]')] break ## Additionally, we have no good way of communicating lists to vim, so we have ## to turn in into one long string, which can be problematic if e.g. the @@ -295,9 +313,12 @@ except ValueError: for c, m in zip(completions, metadata): if 'CALLSIG' in m: split = m.partition('CALLSIG') - vim.command('call add(res, {"word": pyeval("c"), "menu": pyeval("split[0]"), "info": pyeval("split[-1]")})') + vim.command('call add(res, {"word": IPythonPyeval("c"), ' + '"menu": IPythonPyeval("split[0]"), ' + '"info": IPythonPyeval("split[-1]")})') else: - vim.command('call add(res, {"word": pyeval("c"), "menu": pyeval("m")})') + vim.command('call add(res, {"word": IPythonPyeval("c"), ' + '"menu": IPythonPyeval("m")})') endpython "call extend(res,completions) return res @@ -307,7 +328,7 @@ endpython function! IPythonHistory(pattern, ...) let session = a:0 > 0 ? a:1 : (-1) let res = [] - python << endpython + Python2or3 << endpython n = vim.vars.get('ipython_history_len', 100) pattern = vim.eval('a:pattern') if pattern: @@ -326,9 +347,9 @@ for session, line, code in reversed(history): if not unique or code.strip() not in seen: seen.add(code.strip()) vim.command('call add(res, {' - '"session": +pyeval("session"), ' - '"line": +pyeval("line"), ' - '"code": pyeval("code")})') + '"session": +IPythonPyeval("session"), ' + '"line": +IPythonPyeval("line"), ' + '"code": IPythonPyeval("code")})') endpython return res endfunction diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 13d21f2..a47f86a 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -5,6 +5,7 @@ try: from queue import Empty # python3 convention + unicode = str except ImportError: from Queue import Empty @@ -20,6 +21,27 @@ def __getattribute__(self, key): import os import sys import time +PY3 = sys.version_info[0] == 3 + +class VimVars(object): + + """Wrapper for vim.vars for converting bytes to str.""" + + def get(self, name, default=None): + var = vim.vars.get(name, default) + if PY3 and isinstance(var, bytes): + var = str(var, vim_encoding) + return var + + def __getitem__(self, name): + if name not in vim.vars: + raise KeyError(name) + return self.get(name) + + def __setitem__(self, name, value): + vim.vars[name] = value + +vim_vars = VimVars() # get around unicode problems when interfacing with vim vim_encoding=vim.eval('&encoding') or 'utf-8' @@ -94,7 +116,10 @@ def new_ipy(s=''): new_ipy() """ - from IPython.kernel import KernelManager + try: + from jupyter_client import KernelManager + except ImportError: + from IPython.kernel import KernelManager km = KernelManager() km.start_kernel() return km_from_string(km.connection_file) @@ -108,23 +133,26 @@ def km_from_string(s=''): import IPython except ImportError: raise ImportError("Could not find IPython. " + _install_instructions) - from IPython.config.loader import KeyValueConfigLoader try: - from IPython.kernel import ( - KernelManager, - find_connection_file, - ) + from traitlets.config.loader import KeyValueConfigLoader + except ImportError: + from IPython.config.loader import KeyValueConfigLoader + try: + from jupyter_client import KernelManager, find_connection_file except ImportError: - # IPython < 1.0 - from IPython.zmq.blockingkernelmanager import BlockingKernelManager as KernelManager - from IPython.zmq.kernelapp import kernel_aliases try: - from IPython.lib.kernel import find_connection_file + from IPython.kernel import KernelManager, find_connection_file except ImportError: - # < 0.12, no find_connection_file - pass + # IPython < 1.0 + from IPython.zmq.blockingkernelmanager import BlockingKernelManager as KernelManager + from IPython.zmq.kernelapp import kernel_aliases + try: + from IPython.lib.kernel import find_connection_file + except ImportError: + # < 0.12, no find_connection_file + pass - global km, kc, send, history + global km, kc, send, history, complete, object_info # Test if connection is still alive connected = False @@ -196,13 +224,10 @@ def km_from_string(s=''): kc = km kc.start_channels() - try: - send = kc.execute - history = kc.history - except AttributeError: - # < 3.0 - send = kc.shell_channel.execute - history = kc.shell_channel.history + send = kc.execute if hasattr(kc, 'execute') else kc.shell_channel.execute + history = kc.history if hasattr(kc, 'history') else kc.shell_channel.history + complete = kc.complete if hasattr(kc, 'complete') else kc.shell_channel.complete + object_info = kc.inspect if hasattr(kc, 'inspect') else kc.shell_channel.object_info send('', silent=True) try: @@ -212,19 +237,6 @@ def km_from_string(s=''): echo("IPython connection attempt #%d failed - no messages" % attempt, "Warning") continue - #XXX: backwards compatibility for IPython < 0.13 - try: - import inspect - sc = kc.shell_channel - num_oinfo_args = len(inspect.getargspec(sc.object_info).args) - if num_oinfo_args == 2: - # patch the object_info method which used to only take one argument - klass = sc.__class__ - klass._oinfo_orig = klass.object_info - klass.object_info = lambda s,x,y: s._oinfo_orig(x) - except: - pass - #XXX: backwards compatibility for IPython < 1.0 if not hasattr(kc, 'iopub_channel'): kc.iopub_channel = kc.sub_channel @@ -239,14 +251,15 @@ def km_from_string(s=''): send('"_vim_client";_=_;__=__', store_history=False) #XXX: backwards compatibility for IPython < 0.13 - import inspect sc = kc.shell_channel - num_oinfo_args = len(inspect.getargspec(sc.object_info).args) - if num_oinfo_args == 2: - # patch the object_info method which used to only take one argument - klass = sc.__class__ - klass._oinfo_orig = klass.object_info - klass.object_info = lambda s,x,y: s._oinfo_orig(x) + if hasattr(sc, 'object_info'): + import inspect + num_oinfo_args = len(inspect.getargspec(sc.object_info).args) + if num_oinfo_args == 2: + # patch the object_info method which used to only take one argument + klass = sc.__class__ + klass._oinfo_orig = klass.object_info + klass.object_info = lambda s,x,y: s._oinfo_orig(x) #XXX: backwards compatibility for IPython < 1.0 if not hasattr(kc, 'iopub_channel'): @@ -289,7 +302,7 @@ def disconnect(): def get_doc(word, level=0): if kc is None: return ["Not connected to IPython, cannot query: %s" % word] - msg_id = kc.shell_channel.object_info(word, level) + msg_id = object_info(word, detail_level=level) doc = get_doc_msg(msg_id) # get around unicode problems when interfacing with vim return [d.encode(vim_encoding) for d in doc] @@ -398,9 +411,12 @@ def ipy_complete(base, current_line, pos): else: current_line = current_line[pos-len(base):pos] pos = len(base) - msg_id = kc.shell_channel.complete(base, current_line, pos) try: - m = get_child_msg(msg_id, timeout=vim.vars.get('ipython_completion_timeout', 2)) + msg_id = complete(text=base, line=current_line, cursor_pos=pos) + except TypeError: + msg_id = complete(code=current_line, cursor_pos=pos) + try: + m = get_child_msg(msg_id, timeout=vim_vars.get('ipython_completion_timeout', 2)) matches = m['content']['matches'] metadata = m['content']['metadata'] # we need to be careful with unicode, because we can have unicode @@ -599,22 +615,22 @@ def run_this_file(): if ext in ('pxd', 'pxi', 'pyx', 'pyxbld'): cmd = ' '.join(filter(None, ( '%run_cython', - vim.vars.get('cython_run_flags', ''), + vim_vars.get('cython_run_flags', ''), repr(vim.current.buffer.name)))) else: - cmd = '%%run %s %s' % (vim.vars['ipython_run_flags'], + cmd = '%%run %s %s' % (vim_vars['ipython_run_flags'], repr(vim.current.buffer.name)) msg_id = send(cmd) print_prompt(cmd, msg_id) @with_subchannel def run_ipy_input(): - lines = vim.eval('g:ipy_input') + lines = vim_vars['ipy_input'] if lines.strip().endswith('?'): return get_doc_buffer(level=1 if lines.strip().endswith('??') else 0, word=lines.strip().rstrip('?')) - msg_id = send(lines, store_history=vim.vars.get('ipython_store_history', True)) - lines = unicode(lines, 'utf-8').replace('\n', u'\xac') + msg_id = send(lines, store_history=vim_vars.get('ipython_store_history', True)) + lines = lines.replace('\n', u'\xac') print_prompt(lines[:(int(vim.options['columns']) - 22)], msg_id) @with_subchannel @@ -708,14 +724,15 @@ def set_pid(): def eval_ipy_input(var=None): - if not vim.vars.get('ipy_input', None): + ipy_input = vim_vars['ipy_input'] + if not ipy_input: return - if vim.vars['ipy_input'].startswith(('%', '!', '$')): + if ipy_input.startswith(('%', '!', '$')): msg_id = send('', silent=True, - user_expressions={'_expr': vim.vars['ipy_input']}) + user_expressions={'_expr': ipy_input}) else: msg_id = send('from __future__ import division; ' - '_expr = %s' % vim.vars['ipy_input'], silent=True, + '_expr = %s' % ipy_input, silent=True, user_expressions={'_expr': '_expr'}) try: child = get_child_msg(msg_id) @@ -726,12 +743,14 @@ def eval_ipy_input(var=None): try: text = result['_expr']['data']['text/plain'] if var: - from cStringIO import StringIO + try: + from cStringIO import StringIO + except ImportError: + from io import StringIO from tokenize import STRING, generate_tokens - if generate_tokens(StringIO( - text.encode(vim_encoding)).readline).next()[0] == STRING: + if next(generate_tokens(StringIO(text).readline))[0] == STRING: from ast import parse - vim.vars[var.replace('g:', '')] = parse(text).body[0].value.s + vim_vars[var.replace('g:', '')] = parse(text).body[0].value.s else: vim.command('let %s = "%s"' % (var, text.replace('"', '\\"'))) else: @@ -827,10 +846,10 @@ def get_history(n, pattern=None, unique=True): msg_id = history( hist_access_type='search' if pattern else 'tail', pattern=pattern, n=n, unique=unique, - raw=vim.vars.get('ipython_history_raw', True)) + raw=vim_vars.get('ipython_history_raw', True)) try: child = get_child_msg( - msg_id, timeout=float(vim.vars.get('ipython_history_timeout', 2))) + msg_id, timeout=float(vim_vars.get('ipython_history_timeout', 2))) results = [(session, line, code.encode(vim_encoding)) for session, line, code in child['content']['history']] except Empty: @@ -841,25 +860,25 @@ def get_history(n, pattern=None, unique=True): return results def get_session_history(session=None, pattern=None): + from ast import literal_eval + from fnmatch import fnmatch msg_id = send('', silent=True, user_expressions={ '_hist': '[h for h in get_ipython().history_manager.get_range(' '%s, raw=%s)]' % (str(session) if session else 'get_ipython().history_manager.session_number', - vim.vars.get('ipython_history_raw', 'True')), + vim_vars.get('ipython_history_raw', 'True')), '_session': 'get_ipython().history_manager.session_number', }) try: child = get_child_msg( - msg_id, timeout=float(vim.vars.get('ipython_history_timeout', 2))) + msg_id, timeout=float(vim_vars.get('ipython_history_timeout', 2))) hist = child['content']['user_expressions']['_hist'] session = child['content']['user_expressions']['_session'] session = int(session['data']['text/plain'].encode(vim_encoding)) - from ast import literal_eval - from fnmatch import fnmatch - more = literal_eval(hist['data']['text/plain'].encode(vim_encoding)) - return [(s if s > 0 else session, l, c) for s, l, c in more - if fnmatch(c, pattern or '*')] + hist = literal_eval(hist['data']['text/plain']) + return [(s if s > 0 else session, l, c.encode(vim_encoding)) + for s, l, c in hist if fnmatch(c, pattern or '*')] except Empty: echo("no reply from IPython kernel") return [] From 7bcea4df93390a7576dddd204982ce7b1622c200 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 1 Nov 2015 18:19:43 -0700 Subject: [PATCH 070/122] Fix completion of imports and magics --- ftplugin/python/ipy.vim | 51 +++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 49b24bc..2ab25be 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -230,39 +230,39 @@ else endif fun! CompleteIPython(findstart, base) - if a:findstart + if a:findstart + " return immediately for imports + if getline('.')[:col('.')-1] =~# + \ '\v^\s*(from\s+\w+\s+import\s+(\w+,\s+)*|import\s+)' + let s:start = col('.') - 1 + Python2or3 current_line = vim.current.line + return col('.') - 1 + endif " locate the start of the word let line = split(getline('.')[:col('.')-1], '\zs') - let s:start = strchars(getline('.')[:col('.')-1]) - 1 - if line[s:start-1] !~ s:split_pattern && + let s:start = col('.') - 1 + if s:start == 0 || (len(line) == s:start && + \ line[s:start-2] !~ s:split_pattern && \ !(g:ipython_greedy_matching && s:start >= 2 - \ && line[s:start-2] =~ '\k') && - \ join(line[s:start-2:s:start-1], '') !=# '].' - if line =~# '\v^\s*from\s+\w+\s+import\s+(\w+,\s+)*' - Python2or3 << endpython -current_line = vim.current.line -endpython - return col('.') - 1 - else - return -1 - endif + \ && line[s:start-3] =~ '\k') && + \ join(line[s:start-3:s:start-2], '') !=# '].') + return -1 endif + let s:start = strchars(getline('.')[:col('.')-1]) - 1 while s:start > 0 && (line[s:start-1] =~ s:split_pattern \ || (g:ipython_greedy_matching && line[s:start-1] == '.' \ && s:start >= 2 && line[s:start-2] =~ '\k') \ || join(line[s:start-2:s:start-1], '') ==# '].') - if g:ipython_greedy_matching && line[s:start-1] == '[' && - \ (s:start == 1 || line[s:start-2] !~ '\k\|\]') - break - endif - let s:start -= 1 + if g:ipython_greedy_matching && line[s:start-1] == '[' && + \ (s:start == 1 || line[s:start-2] !~ '\k\|\]') + break + endif + let s:start -= 1 endwhile - Python2or3 << endpython -current_line = vim.current.line -endpython - return s:start + len(join(line[: s:start], '')) - \ - len(getline('.')[: s:start]) - else + Python2or3 current_line = vim.current.line + return s:start + len(join(line[: s:start], '')) - + \ len(getline('.')[: s:start]) + else " find months matching with "a:base" let res = [] let start = s:start @@ -307,7 +307,8 @@ if vim.vars['ipython_dictionary_completion'] and not vim.vars['ipython_greedy_ma ## include the problematic match, instead of not including anything. There's a ## bit more indirection here, but I think it's worth it try: - completions, metadata = zip(*sorted(zip(completions, metadata), key=lambda x: x[0].lower())) + completions, metadata = zip(*sorted(zip(completions, metadata), + key=lambda x: x[0].lstrip('%').lower())) except ValueError: pass for c, m in zip(completions, metadata): From 90e59d59d898ada085a5af5ab6df995afb0c9787 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 6 Nov 2015 20:09:53 -0700 Subject: [PATCH 071/122] =?UTF-8?q?Fix=20'from=20x.x=20=E2=80=A6'=20import?= =?UTF-8?q?s=20and=20fix=20imports=20s:start?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ftplugin/python/ipy.vim | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 2ab25be..a6db08a 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -233,10 +233,14 @@ fun! CompleteIPython(findstart, base) if a:findstart " return immediately for imports if getline('.')[:col('.')-1] =~# - \ '\v^\s*(from\s+\w+\s+import\s+(\w+,\s+)*|import\s+)' + \ '\v^\s*(from\s+\w+(\.\w+)*\s+import\s+(\w+,\s+)*|import\s+)' + let line = getline('.') let s:start = col('.') - 1 + while s:start && line[s:start - 1] =~ '[._[:alnum:]]' + let s:start -= 1 + endwhile Python2or3 current_line = vim.current.line - return col('.') - 1 + return s:start endif " locate the start of the word let line = split(getline('.')[:col('.')-1], '\zs') @@ -321,10 +325,9 @@ for c, m in zip(completions, metadata): vim.command('call add(res, {"word": IPythonPyeval("c"), ' '"menu": IPythonPyeval("m")})') endpython - "call extend(res,completions) return res - endif - endfun + endif +endfun function! IPythonHistory(pattern, ...) let session = a:0 > 0 ? a:1 : (-1) From 40dc8da77ba491678ed11a4d081738beb2137a40 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 6 Nov 2015 20:10:13 -0700 Subject: [PATCH 072/122] Return empty list after aborting completion --- ftplugin/python/ipy.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index a6db08a..11d35e4 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -250,7 +250,8 @@ fun! CompleteIPython(findstart, base) \ !(g:ipython_greedy_matching && s:start >= 2 \ && line[s:start-3] =~ '\k') && \ join(line[s:start-3:s:start-2], '') !=# '].') - return -1 + let s:start = -1 + return s:start endif let s:start = strchars(getline('.')[:col('.')-1]) - 1 while s:start > 0 && (line[s:start-1] =~ s:split_pattern @@ -269,6 +270,7 @@ fun! CompleteIPython(findstart, base) else " find months matching with "a:base" let res = [] + if s:start == -1 | return [] | endif let start = s:start Python2or3 << endpython base = vim.eval("a:base") From a2ade82730f6432bd8e363b99a920505200a0cec Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 8 Nov 2015 17:22:05 -0700 Subject: [PATCH 073/122] Use g:ipython_store_history everywhere --- ftplugin/python/vim_ipython.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index a47f86a..0ac8a1d 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -224,11 +224,18 @@ def km_from_string(s=''): kc = km kc.start_channels() - send = kc.execute if hasattr(kc, 'execute') else kc.shell_channel.execute + execute = kc.execute if hasattr(kc, 'execute') else kc.shell_channel.execute history = kc.history if hasattr(kc, 'history') else kc.shell_channel.history complete = kc.complete if hasattr(kc, 'complete') else kc.shell_channel.complete object_info = kc.inspect if hasattr(kc, 'inspect') else kc.shell_channel.object_info + def send(msg, **kwargs): + kwds = dict( + store_history=vim_vars.get('ipython_store_history', True), + ) + kwds.update(kwargs) + return execute(msg, **kwds) + send('', silent=True) try: msg = kc.shell_channel.get_msg(timeout=1) @@ -629,7 +636,7 @@ def run_ipy_input(): if lines.strip().endswith('?'): return get_doc_buffer(level=1 if lines.strip().endswith('??') else 0, word=lines.strip().rstrip('?')) - msg_id = send(lines, store_history=vim_vars.get('ipython_store_history', True)) + msg_id = send(lines) lines = lines.replace('\n', u'\xac') print_prompt(lines[:(int(vim.options['columns']) - 22)], msg_id) From f9d238be7f2b5bb77f4924447c78387b1c20ea32 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 11 Nov 2015 22:01:06 -0700 Subject: [PATCH 074/122] Fix unicode errors in run_ipy_input in Python 2 --- ftplugin/python/vim_ipython.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 0ac8a1d..9636910 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -31,6 +31,8 @@ def get(self, name, default=None): var = vim.vars.get(name, default) if PY3 and isinstance(var, bytes): var = str(var, vim_encoding) + elif not PY3 and isinstance(var, str): + var = unicode(var, vim_encoding) return var def __getitem__(self, name): From 4fffbbbcd39625c5dfcdc64d9c3fee9c8acddd16 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 29 Nov 2015 17:28:47 -0700 Subject: [PATCH 075/122] Add silent keyword argument to run_ipy_input --- ftplugin/python/vim_ipython.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 9636910..e86bb2f 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -607,11 +607,11 @@ def print_prompt(prompt,msg_id=None): else: echo("In[]: %s" % prompt) -def with_subchannel(f,*args): +def with_subchannel(f,*args,**kwargs): "conditionally monitor subchannel" def f_with_update(*args): try: - f(*args) + f(*args,**kwargs) if monitor_subchannel: update_subchannel_msgs(force=True) except AttributeError: #if kc is None @@ -633,12 +633,12 @@ def run_this_file(): print_prompt(cmd, msg_id) @with_subchannel -def run_ipy_input(): +def run_ipy_input(silent=False): lines = vim_vars['ipy_input'] if lines.strip().endswith('?'): return get_doc_buffer(level=1 if lines.strip().endswith('??') else 0, word=lines.strip().rstrip('?')) - msg_id = send(lines) + msg_id = send(lines, silent=silent) lines = lines.replace('\n', u'\xac') print_prompt(lines[:(int(vim.options['columns']) - 22)], msg_id) From 935237eadb32074ce0a8d4e1c79bc37db20403c2 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 24 Dec 2015 11:36:24 -0700 Subject: [PATCH 076/122] MAke get_child_msg timeout configurable --- ftplugin/python/vim_ipython.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index e86bb2f..dadd5fd 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -579,8 +579,10 @@ def update_subchannel_msgs(debug=False, force=False): vim.command(str(currentwin) + 'wincmd w') return update_occured -def get_child_msg(msg_id, timeout=1): +def get_child_msg(msg_id, timeout=None): # XXX: message handling should be split into its own process in the future + if timeout is None: + timeout = float(vim_vars.get('ipython_timeout', 1)) while True: # get_msg will raise with Empty exception if no messages arrive in 1 second m = kc.shell_channel.get_msg(timeout=timeout) From 2e61840d33a496a52690bbf8e4c81c8b262a60e5 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 24 Dec 2015 11:36:53 -0700 Subject: [PATCH 077/122] Add command line completion function --- ftplugin/python/ipy.vim | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 11d35e4..fd067b1 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -359,3 +359,23 @@ for session, line, code in reversed(history): endpython return res endfunction + +function! IPythonCmdComplete(arglead, cmdline, cursorpos) +Python2or3 << endpython +arglead = vim.eval('a:arglead') +if ' ' in arglead and not (arglead.strip().startswith('from ') or + arglead.strip().startswith('import ')): + start = arglead.split()[-1] +else: + start = arglead + +matches, _ = ipy_complete(start, + vim.eval('a:cmdline'), + int(vim.eval('a:cursorpos'))) + +if ' ' in arglead: + arglead = arglead.rpartition(' ')[0] + matches = ['%s %s' % (arglead, m) for m in matches] +vim.command('return IPythonPyeval("matches")') +endpython +endfunction From e27236b92f4ddc5132eae7f1e70bdc4579cf5eb0 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 25 Dec 2015 16:19:53 -0700 Subject: [PATCH 078/122] Fix return value for failed completion --- ftplugin/python/ipy.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index fd067b1..82a8301 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -281,7 +281,7 @@ except IOError: vim.command('setlocal omnifunc=jedi#completions') else: vim.command('setlocal omnifunc=') - vim.command('return -1') + vim.command('return []') # we need to be careful with unicode, because we can have unicode # completions for filenames (for the %run magic, for example). So the next # line will fail on those: From 515c45e482a96940efaf6ab89da93a662a2e2ea2 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 30 Dec 2015 20:37:46 -0700 Subject: [PATCH 079/122] Add whitespace delimited completion function --- ftplugin/python/ipy.vim | 109 ++++++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 49 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 82a8301..6c6a594 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -81,6 +81,9 @@ import re vim_ipython_path = vim.eval("expand(':h')") sys.path.append(vim_ipython_path) from vim_ipython import * + +class Result(object): + pass endpython fun! toggle_send_on_save() @@ -229,6 +232,35 @@ else let s:split_pattern = '\k\|\.' endif +Python2or3 << endpython +def process_matches(matches, metadata, result): + if PY3: + completions = matches + else: + completions = [s.encode(vim_encoding) for s in matches] + metadata = [s.encode(vim_encoding) for s in metadata] + if vim.vars['ipython_dictionary_completion'] and not vim.vars['ipython_greedy_matching']: + for char in '\'"': + if any(c.endswith(char + ']') for c in completions): + completions = [c for c in completions if c.endswith(char + ']')] + break + try: + completions, metadata = zip(*sorted(zip(completions, metadata), + key=lambda x: x[0].lstrip('%').lower())) + except ValueError: + pass + for c, m in zip(completions, metadata): + result.c, result.m = c, m + if 'CALLSIG' in m: + result.split = m.partition('CALLSIG') + vim.command('call add(res, {"word": IPythonPyeval("r.c"), ' + '"menu": IPythonPyeval("r.split[0]"), ' + '"info": IPythonPyeval("r.split[-1]")})') + else: + vim.command('call add(res, {"word": IPythonPyeval("r.c"), ' + '"menu": IPythonPyeval("r.m")})') +endpython + fun! CompleteIPython(findstart, base) if a:findstart " return immediately for imports @@ -282,50 +314,8 @@ except IOError: else: vim.command('setlocal omnifunc=') vim.command('return []') -# we need to be careful with unicode, because we can have unicode -# completions for filenames (for the %run magic, for example). So the next -# line will fail on those: -#completions= [str(u) for u in matches] -# because str() won't work for non-ascii characters -# and we also have problems with unicode in vim, hence the following: -if PY3: - completions = matches -else: - completions = [s.encode(vim_encoding) for s in matches] - metadata = [s.encode(vim_encoding) for s in metadata] -if vim.vars['ipython_dictionary_completion'] and not vim.vars['ipython_greedy_matching']: - for c in completions: - if c.endswith("']"): - completions = [c for c in completions if c.endswith("']")] - break - elif c.endswith('"]'): - completions = [c for c in completions if c.endswith('"]')] - break -## Additionally, we have no good way of communicating lists to vim, so we have -## to turn in into one long string, which can be problematic if e.g. the -## completions contain quotes. The next line will not work if some filenames -## contain quotes - but if that's the case, the user's just asking for -## it, right? -#completions = '["'+ '", "'.join(completions)+'"]' -#vim.command("let completions = %s" % completions) -## An alternative for the above, which will insert matches one at a time, so -## if there's a problem with turning a match into a string, it'll just not -## include the problematic match, instead of not including anything. There's a -## bit more indirection here, but I think it's worth it -try: - completions, metadata = zip(*sorted(zip(completions, metadata), - key=lambda x: x[0].lstrip('%').lower())) -except ValueError: - pass -for c, m in zip(completions, metadata): - if 'CALLSIG' in m: - split = m.partition('CALLSIG') - vim.command('call add(res, {"word": IPythonPyeval("c"), ' - '"menu": IPythonPyeval("split[0]"), ' - '"info": IPythonPyeval("split[-1]")})') - else: - vim.command('call add(res, {"word": IPythonPyeval("c"), ' - '"menu": IPythonPyeval("m")})') +r = Result() # result object to let vim access namespace while in a function +process_matches(matches, metadata, r) endpython return res endif @@ -360,7 +350,8 @@ endpython return res endfunction -function! IPythonCmdComplete(arglead, cmdline, cursorpos) +function! IPythonCmdComplete(arglead, cmdline, cursorpos, ...) + let res = [] Python2or3 << endpython arglead = vim.eval('a:arglead') if ' ' in arglead and not (arglead.strip().startswith('from ') or @@ -369,13 +360,33 @@ if ' ' in arglead and not (arglead.strip().startswith('from ') or else: start = arglead -matches, _ = ipy_complete(start, - vim.eval('a:cmdline'), - int(vim.eval('a:cursorpos'))) +matches, metadata = ipy_complete(start, + vim.eval('a:cmdline'), + int(vim.eval('a:cursorpos'))) if ' ' in arglead: arglead = arglead.rpartition(' ')[0] matches = ['%s %s' % (arglead, m) for m in matches] -vim.command('return IPythonPyeval("matches")') +if int(vim.eval('a:0')): + r = Result() + process_matches(matches, metadata, r) endpython + if a:0 + return res + else + return IPythonPyeval('matches') + endif +endfunction + +function! GreedyCompleteIPython(findstart, base) + if a:findstart + let line = getline('.') + let start = col('.') - 1 + while start && line[start - 1] =~ '\S' + let start -= 1 + endwhile + return start + else + return IPythonCmdComplete(a:base, a:base, len(a:base), 1) + endif endfunction From bb475707be87c7528254f65798c324f6dd108c76 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 3 Jan 2016 17:44:41 -0700 Subject: [PATCH 080/122] Handle no response from kernel correctly --- ftplugin/python/ipy.vim | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 6c6a594..1f26644 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -360,9 +360,12 @@ if ' ' in arglead and not (arglead.strip().startswith('from ') or else: start = arglead -matches, metadata = ipy_complete(start, - vim.eval('a:cmdline'), - int(vim.eval('a:cursorpos'))) +try: + matches, metadata = ipy_complete(start, + vim.eval('a:cmdline'), + int(vim.eval('a:cursorpos'))) +except IOError: + vim.command('return []') if ' ' in arglead: arglead = arglead.rpartition(' ')[0] From 82234cc813113e475f92fd358ef0b886baadadc7 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 3 Jan 2016 21:06:29 -0700 Subject: [PATCH 081/122] Fix handling of string escapes in eval_ipy_input --- ftplugin/python/vim_ipython.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index dadd5fd..d9e2d85 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -753,6 +753,8 @@ def eval_ipy_input(var=None): result = child['content']['user_expressions'] try: text = result['_expr']['data']['text/plain'] + if not PY3 and isinstance(text, str): + text = unicode(text, vim_encoding) if var: try: from cStringIO import StringIO @@ -763,9 +765,11 @@ def eval_ipy_input(var=None): from ast import parse vim_vars[var.replace('g:', '')] = parse(text).body[0].value.s else: - vim.command('let %s = "%s"' % (var, text.replace('"', '\\"'))) + vim.command('let %s = "%s"' % ( + var, text.replace('\\', '\\\\').replace('"', '\\"'))) else: - vim.command('call setreg(\'"\', "%s")' % text.replace('"', '\\"')) + vim.command('call setreg(\'"\', "%s")' % + text.replace('\\', '\\\\').replace('"', '\\"')) except KeyError: try: try: From d8babdae90df4ace9859457831c745c627e486a3 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 6 Jan 2016 23:21:17 -0700 Subject: [PATCH 082/122] Ignore consecutive duplicates in IPythonHistory --- ftplugin/python/ipy.vim | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 1f26644..0cc11ba 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -77,7 +77,8 @@ endif Python2or3 << endpython import vim import sys -import re +import itertools as it +import operator as op vim_ipython_path = vim.eval("expand(':h')") sys.path.append(vim_ipython_path) from vim_ipython import * @@ -339,7 +340,9 @@ if int(vim.eval('session')) >= 0: else: history = get_history(n, pattern=pattern, unique=unique) seen = set() -for session, line, code in reversed(history): +for session, line, code in reversed( + [next(iter(h)) for _, h in it.groupby( + history, lambda i: (i[0], i[2]))]): if not unique or code.strip() not in seen: seen.add(code.strip()) vim.command('call add(res, {' From b28f511640dc9ca1245f34225c64541eba3694d2 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sat, 16 Jan 2016 20:17:12 -0700 Subject: [PATCH 083/122] Make uniqueness optional in IPythonHistory --- ftplugin/python/ipy.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 0cc11ba..542755c 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -333,7 +333,8 @@ if pattern: pattern = '*{0}*'.format(pattern) else: pattern = None -unique = pattern is not None +unique = vim.eval('get(g:, "ipython_history_unique", "")') +unique = bool(int(unique)) if unique else pattern is not None if int(vim.eval('session')) >= 0: history = get_session_history(session=int(vim.eval('session')), pattern=pattern) From 848b0e7d71dda065d8b20c2797132db543717d5d Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 27 Jan 2016 22:36:21 -0700 Subject: [PATCH 084/122] Show most recent repeated history item --- ftplugin/python/ipy.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 542755c..ff149e6 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -342,7 +342,7 @@ else: history = get_history(n, pattern=pattern, unique=unique) seen = set() for session, line, code in reversed( - [next(iter(h)) for _, h in it.groupby( + [list(h)[-1] for _, h in it.groupby( history, lambda i: (i[0], i[2]))]): if not unique or code.strip() not in seen: seen.add(code.strip()) From eef6e30445bac54a7281c0f1e26766756073f44b Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sat, 13 Feb 2016 18:30:10 -0700 Subject: [PATCH 085/122] Allow keyword arguments to run_ipy_input --- ftplugin/python/vim_ipython.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index d9e2d85..cbdf353 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -611,7 +611,7 @@ def print_prompt(prompt,msg_id=None): def with_subchannel(f,*args,**kwargs): "conditionally monitor subchannel" - def f_with_update(*args): + def f_with_update(*args, **kwargs): try: f(*args,**kwargs) if monitor_subchannel: @@ -635,12 +635,12 @@ def run_this_file(): print_prompt(cmd, msg_id) @with_subchannel -def run_ipy_input(silent=False): +def run_ipy_input(**kwargs): lines = vim_vars['ipy_input'] if lines.strip().endswith('?'): return get_doc_buffer(level=1 if lines.strip().endswith('??') else 0, word=lines.strip().rstrip('?')) - msg_id = send(lines, silent=silent) + msg_id = send(lines, **kwargs) lines = lines.replace('\n', u'\xac') print_prompt(lines[:(int(vim.options['columns']) - 22)], msg_id) From 5af8ab8c0bbb9cf04af761b0cf2bb68e746ab664 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 19 Feb 2016 19:34:08 -0700 Subject: [PATCH 086/122] Set defaults for history options --- ftplugin/python/ipy.vim | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index ff149e6..5737ae4 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -61,6 +61,19 @@ if !exists('g:ipy_autostart') let g:ipy_autostart = 1 endif +if !exists('g:ipython_history_len') + let g:ipython_history_len = 100 +endif +if !exists('g:ipython_history_raw') + let g:ipython_history_raw = 1 +endif +if !exists('g:ipython_history_unique') + let g:ipython_history_unique = 1 +endif +if !exists('g:ipython_history_timeout') + let g:ipython_history_timeout = 2 +endif + " Register IPython completefunc " 'global' -- for all of vim (default). " 'local' -- only for the current buffer. From 26f4ca6471cfc9c1f6b641090653713dd3917163 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 24 Feb 2016 21:43:51 -0700 Subject: [PATCH 087/122] Escape opening square brackets in IPythonHistory --- ftplugin/python/ipy.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 5737ae4..8f7f152 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -344,6 +344,7 @@ pattern = vim.eval('a:pattern') if pattern: if not pattern.startswith('*') and not pattern.endswith('*'): pattern = '*{0}*'.format(pattern) + pattern = pattern.replace('[', '[[]') else: pattern = None unique = vim.eval('get(g:, "ipython_history_unique", "")') From 5cfb2dbc8e1e19a04a55bf413b3e61e4592db64d Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 9 Mar 2016 19:22:48 -0700 Subject: [PATCH 088/122] Add map to %run with -n flag --- ftplugin/python/ipy.vim | 2 ++ ftplugin/python/vim_ipython.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 8f7f152..367ee2e 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -147,6 +147,7 @@ augroup END " Setup plugin mappings for the most common ways to interact with ipython. noremap (IPython-RunFile) :update:Python2or3 run_this_file() +noremap (IPython-ImportFile) :update:Python2or3 run_this_file('-n') noremap (IPython-RunLine) :Python2or3 run_this_line() noremap (IPython-RunLines) :Python2or3 run_these_lines() noremap (IPython-OpenPyDoc) :Python2or3 get_doc_buffer() @@ -168,6 +169,7 @@ function! s:DoMappings() if g:ipy_perform_mappings != 0 if &buftype == '' map (IPython-RunFile) + map g (IPython-ImportFile) endif " map (IPython-RunLine) map (IPython-RunLines) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index cbdf353..55623fe 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -621,7 +621,7 @@ def f_with_update(*args, **kwargs): return f_with_update @with_subchannel -def run_this_file(): +def run_this_file(flags=''): ext = os.path.splitext(vim.current.buffer.name)[-1][1:] if ext in ('pxd', 'pxi', 'pyx', 'pyxbld'): cmd = ' '.join(filter(None, ( @@ -629,7 +629,7 @@ def run_this_file(): vim_vars.get('cython_run_flags', ''), repr(vim.current.buffer.name)))) else: - cmd = '%%run %s %s' % (vim_vars['ipython_run_flags'], + cmd = '%%run %s %s' % (flags or vim_vars['ipython_run_flags'], repr(vim.current.buffer.name)) msg_id = send(cmd) print_prompt(cmd, msg_id) From 206a238de911ba7c1741abf24c3958b320e11ca3 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sat, 2 Apr 2016 10:40:26 -0700 Subject: [PATCH 089/122] Handle no metadata --- ftplugin/python/vim_ipython.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 55623fe..c193ed7 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -427,7 +427,9 @@ def ipy_complete(base, current_line, pos): try: m = get_child_msg(msg_id, timeout=vim_vars.get('ipython_completion_timeout', 2)) matches = m['content']['matches'] - metadata = m['content']['metadata'] + metadata = m['content'].get('metadata', None) + if not metadata: + metadata = ['' for _ in matches] # we need to be careful with unicode, because we can have unicode # completions for filenames (for the %run magic, for example). So the next # line will fail on those: From c592d709782fc5ef0048cef27423fb6e5bceae12 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 3 Apr 2016 18:22:39 -0700 Subject: [PATCH 090/122] Better handling of brackets in complete findstart Example test cases: data1[test[0]][' items[items['enum']<50]. items[items[' data3['aspernatur'][' --- ftplugin/python/ipy.vim | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 367ee2e..bb4805a 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -302,13 +302,19 @@ fun! CompleteIPython(findstart, base) return s:start endif let s:start = strchars(getline('.')[:col('.')-1]) - 1 + let bracket_level = 0 while s:start > 0 && (line[s:start-1] =~ s:split_pattern \ || (g:ipython_greedy_matching && line[s:start-1] == '.' \ && s:start >= 2 && line[s:start-2] =~ '\k') \ || join(line[s:start-2:s:start-1], '') ==# '].') - if g:ipython_greedy_matching && line[s:start-1] == '[' && - \ (s:start == 1 || line[s:start-2] !~ '\k\|\]') - break + if g:ipython_greedy_matching && line[s:start-1] == '[' + if (s:start == 1 || line[s:start-2] !~ '\k\|\]') + \ || bracket_level > 0 + break + endif + let bracket_level += 1 + elseif g:ipython_greedy_matching && line[s:start-1] == ']' + let bracket_level -= 1 endif let s:start -= 1 endwhile From 7e418c51565ff0673bf318deaef572858144f296 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 20 Apr 2016 21:01:06 -0700 Subject: [PATCH 091/122] Improve completion edge cases - Allow negative numbers in square brackets - Don't try to complete number literals or incomplete strings --- ftplugin/python/ipy.vim | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index bb4805a..bbe9453 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -306,6 +306,8 @@ fun! CompleteIPython(findstart, base) while s:start > 0 && (line[s:start-1] =~ s:split_pattern \ || (g:ipython_greedy_matching && line[s:start-1] == '.' \ && s:start >= 2 && line[s:start-2] =~ '\k') + \ || (g:ipython_greedy_matching && line[s:start-1] == '-' + \ && s:start >= 2 && line[s:start-2] == '[') \ || join(line[s:start-2:s:start-1], '') ==# '].') if g:ipython_greedy_matching && line[s:start-1] == '[' if (s:start == 1 || line[s:start-2] !~ '\k\|\]') @@ -325,6 +327,10 @@ fun! CompleteIPython(findstart, base) " find months matching with "a:base" let res = [] if s:start == -1 | return [] | endif + " don't complete numeric literals + if a:base =~? '\v^[-+]?\d*\.?\d+(e[-+]?\d+)?\.$' | return [] | endif + " don't complete incomplete string literals + if a:base =~? '\v^(([^''].*)?['']|([^"].*)?["])\.$' | return [] | endif let start = s:start Python2or3 << endpython base = vim.eval("a:base") From c6242746d830c980f2a5bd626b01430937912854 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 20 Apr 2016 21:01:36 -0700 Subject: [PATCH 092/122] Make help requests for magics work right --- ftplugin/python/vim_ipython.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index c193ed7..16ec3a5 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -311,7 +311,15 @@ def disconnect(): def get_doc(word, level=0): if kc is None: return ["Not connected to IPython, cannot query: %s" % word] - msg_id = object_info(word, detail_level=level) + if word.startswith('%'): # request for magic documentation + request = ('_doc = get_ipython().object_inspect("{0}", ' + 'detail_level={1})').format(word, level) + try: + msg_id = send(request, silent=True, user_variables=['_doc']) + except TypeError: # change in IPython 3.0+ + msg_id = send(request, silent=True, user_expressions={'_doc':'_doc'}) + else: + msg_id = object_info(word, detail_level=level) doc = get_doc_msg(msg_id) # get around unicode problems when interfacing with vim return [d.encode(vim_encoding) for d in doc] @@ -331,6 +339,18 @@ def get_doc_msg(msg_id): # timeout occurred return ["no reply from IPython kernel"] + if 'evalue' in content: + return b + + doc = None + if 'user_variables' in content: + doc = content['user_variables']['_doc'] + elif 'user_expressions' in content: + doc = content['user_expressions']['_doc'] + if doc: + import ast + content = ast.literal_eval(doc['data']['text/plain']) + if not content['found']: return b @@ -370,7 +390,7 @@ def get_doc_buffer(level=0, word=None): echo(repr(word)+" not found","Error") return # documentation buffer name is same as the query made to ipython - vim.command('new '+word) + vim.command('new '+word.lstrip('%')) vim.command('setlocal modifiable noro') # doc window quick quit keys: 'q' and 'escape' vim.command('nnoremap q :q') From 8bea17956fc0592c77161d78fcfe6993c13ab96b Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 20 Apr 2016 21:18:53 -0700 Subject: [PATCH 093/122] Escape funcsigs with backticks to not confuse rst --- ftplugin/python/vim_ipython.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 16ec3a5..4722d1e 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -360,6 +360,8 @@ def get_doc_msg(msg_id): text = content['data']['text/plain'] for line in text.split('\n'): b.append(strip_color_escapes(line).rstrip()) + if b[-1].startswith('Signature:'): + b[-1] = re.sub(r'(\s+)(.*)$', r'\1`\2`', b[-1]) return b except KeyError: # no text/plain key return b @@ -369,7 +371,7 @@ def get_doc_msg(msg_id): c = content.get(field,None) if c: if field in ['definition']: - c = strip_color_escapes(c).rstrip() + c = '`%s`' % strip_color_escapes(c).rstrip() s = field.replace('_',' ').title()+':' s = s.ljust(n) if c.find('\n')==-1: From 0c32d06874c046f924c5e8221cc547318595a64d Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 24 Apr 2016 16:28:30 -0700 Subject: [PATCH 094/122] Fix escaping of 'Init signature:' --- ftplugin/python/vim_ipython.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 4722d1e..e6cfce2 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -360,8 +360,9 @@ def get_doc_msg(msg_id): text = content['data']['text/plain'] for line in text.split('\n'): b.append(strip_color_escapes(line).rstrip()) - if b[-1].startswith('Signature:'): - b[-1] = re.sub(r'(\s+)(.*)$', r'\1`\2`', b[-1]) + if 'signature:' in b[-1].lower(): + left, _, right = b[-1].partition(': ') + b[-1] = '{0}: `{1}`'.format(left, right) return b except KeyError: # no text/plain key return b From 15773a098037a44be018fd92d787da809f42fdea Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 29 Apr 2016 20:19:06 -0700 Subject: [PATCH 095/122] Clean up open file descriptors --- ftplugin/python/vim_ipython.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index e6cfce2..d2ed6e3 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -173,12 +173,6 @@ def km_from_string(s=''): continue attempt += 1 - km = KernelManager(connection_file=fullpath) - km.load_connection_file() - - kc = km.client() - kc.start_channels() - if 'connection_file' in KernelManager.class_trait_names(): # 0.12 uses files instead of a collection of ports # include default IPython search path @@ -244,6 +238,7 @@ def send(msg, **kwargs): connected = True except: echo("IPython connection attempt #%d failed - no messages" % attempt, "Warning") + kc.stop_channels() continue #XXX: backwards compatibility for IPython < 1.0 From 67e5046b60b63d70262473049906f30f2780c8d6 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 1 May 2016 09:05:11 -0700 Subject: [PATCH 096/122] Better check for signature line --- ftplugin/python/vim_ipython.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index d2ed6e3..310c760 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -355,7 +355,7 @@ def get_doc_msg(msg_id): text = content['data']['text/plain'] for line in text.split('\n'): b.append(strip_color_escapes(line).rstrip()) - if 'signature:' in b[-1].lower(): + if 'signature: ' in b[-1].lower() and b[-1].endswith(')'): left, _, right = b[-1].partition(': ') b[-1] = '{0}: `{1}`'.format(left, right) return b From 0589ec2a89cf667cefb8bb46a13e4e694c3d6db8 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 29 May 2016 16:38:20 -0700 Subject: [PATCH 097/122] Fix error when null bytes exist in metadata --- ftplugin/python/ipy.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index bbe9453..1703e7b 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -266,6 +266,7 @@ def process_matches(matches, metadata, result): except ValueError: pass for c, m in zip(completions, metadata): + m = m.replace('\0', '^@') # vim can't handle null bytes in Python strings result.c, result.m = c, m if 'CALLSIG' in m: result.split = m.partition('CALLSIG') From 4f39105ee8697acec7c93e5b389d60e5fb585b5f Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 29 May 2016 22:40:16 -0700 Subject: [PATCH 098/122] Delete argspec from doc to fix SyntaxError --- ftplugin/python/vim_ipython.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 310c760..7d03b22 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -308,7 +308,8 @@ def get_doc(word, level=0): return ["Not connected to IPython, cannot query: %s" % word] if word.startswith('%'): # request for magic documentation request = ('_doc = get_ipython().object_inspect("{0}", ' - 'detail_level={1})').format(word, level) + 'detail_level={1})\n' + 'del _doc["argspec"]').format(word, level) try: msg_id = send(request, silent=True, user_variables=['_doc']) except TypeError: # change in IPython 3.0+ From a38a5e4edefbe25a19a7f8164cc89dddeb1f401a Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 12 Jun 2016 15:38:05 -0700 Subject: [PATCH 099/122] Fix function existence check --- ftplugin/python/ipy.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 1703e7b..5078a50 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -338,7 +338,7 @@ base = vim.eval("a:base") try: matches, metadata = ipy_complete(base, current_line, int(vim.eval('start')) + len(base)) except IOError: - if vim.eval('exists("*jedi#completions")'): + if vim.eval('exists("*jedi#completions")') == '1': vim.command('setlocal omnifunc=jedi#completions') else: vim.command('setlocal omnifunc=') From dc67fda500940b0cc153506f7a64cdf3f7bb9d34 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 13 Jul 2016 12:40:18 -0700 Subject: [PATCH 100/122] Fix potential KeyError --- ftplugin/python/vim_ipython.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 7d03b22..7a331e2 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -330,10 +330,11 @@ def get_doc_msg(msg_id): n = 13 # longest field name (empirically) b=[] try: - content = get_child_msg(msg_id)['content'] + m = get_child_msg(msg_id) except Empty: # timeout occurred return ["no reply from IPython kernel"] + content = m['content'] if 'evalue' in content: return b From 021e9c3c6cb8c05d4a2a881189edef45c4759211 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 13 Jul 2016 12:41:22 -0700 Subject: [PATCH 101/122] Use dicts for completion metadata instead of strs --- ftplugin/python/ipy.vim | 21 ++++++++++-------- ftplugin/python/vim_ipython.py | 40 ++++++++++++++++++++++++---------- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 5078a50..f58bd93 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -254,7 +254,8 @@ def process_matches(matches, metadata, result): completions = matches else: completions = [s.encode(vim_encoding) for s in matches] - metadata = [s.encode(vim_encoding) for s in metadata] + for i, m in enumerate(metadata): + metadata[i] = {k: v.encode(vim_encoding) for k, v in m.items()} if vim.vars['ipython_dictionary_completion'] and not vim.vars['ipython_greedy_matching']: for char in '\'"': if any(c.endswith(char + ']') for c in completions): @@ -266,16 +267,18 @@ def process_matches(matches, metadata, result): except ValueError: pass for c, m in zip(completions, metadata): - m = m.replace('\0', '^@') # vim can't handle null bytes in Python strings + # vim can't handle null bytes in Python strings + m = {k: v.replace('\0', '^@') for k, v in m.items()} result.c, result.m = c, m - if 'CALLSIG' in m: - result.split = m.partition('CALLSIG') - vim.command('call add(res, {"word": IPythonPyeval("r.c"), ' - '"menu": IPythonPyeval("r.split[0]"), ' - '"info": IPythonPyeval("r.split[-1]")})') + if 'info' in m: + r.text, r.info = m['text'], m['info'] + vim.command('call add(res, {"word": IPythonPyeval("r.c"), ' + '"menu": IPythonPyeval("r.text"), ' + '"info": IPythonPyeval("r.info")})') else: - vim.command('call add(res, {"word": IPythonPyeval("r.c"), ' - '"menu": IPythonPyeval("r.m")})') + r.text = m.get('text', '') + vim.command('call add(res, {"word": IPythonPyeval("r.c"), ' + '"menu": IPythonPyeval("r.text")})') endpython fun! CompleteIPython(findstart, base) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 7a331e2..a95d206 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -18,6 +18,7 @@ def __getattribute__(self, key): vim = NoOp() print("uh oh, not running inside vim") +import ast import os import sys import time @@ -345,7 +346,6 @@ def get_doc_msg(msg_id): elif 'user_expressions' in content: doc = content['user_expressions']['_doc'] if doc: - import ast content = ast.literal_eval(doc['data']['text/plain']) if not content['found']: @@ -446,20 +446,36 @@ def ipy_complete(base, current_line, pos): msg_id = complete(code=current_line, cursor_pos=pos) try: m = get_child_msg(msg_id, timeout=vim_vars.get('ipython_completion_timeout', 2)) - matches = m['content']['matches'] - metadata = m['content'].get('metadata', None) - if not metadata: - metadata = ['' for _ in matches] - # we need to be careful with unicode, because we can have unicode - # completions for filenames (for the %run magic, for example). So the next - # line will fail on those: - #completions= [str(u) for u in matches] - # because str() won't work for non-ascii characters - # and we also have problems with unicode in vim, hence the following: - return matches, metadata + try: + return get_completion_metadata() + except KeyError: # completion_metadata function not available + matches = m['content']['matches'] + metadata = [{} for _ in matches] + return matches, metadata + except Empty: + echo("no reply from IPython kernel") + raise IOError + +def get_completion_metadata(): + """Generate and fetch completion metadata.""" + request = '_completions = completion_metadata(get_ipython())' + try: + msg_id = send(request, silent=True, user_variables=['_completions']) + except TypeError: # change in IPython 3.0+ + msg_id = send(request, silent=True, user_expressions={'_completions':'_completions'}) + try: + m = get_child_msg(msg_id, timeout=vim_vars.get('ipython_completion_timeout', 2)) except Empty: echo("no reply from IPython kernel") raise IOError + content = m['content'] + if 'user_variables' in content: + metadata = content['user_variables']['_completions'] + else: + metadata = content['user_expressions']['_completions'] + metadata = ast.literal_eval(metadata['data']['text/plain']) + matches = [c['match'] for c in metadata] + return matches, metadata def vim_ipython_is_open(): """ From 655a7dc661ffade77a89b0f9c98f7e1db3df84f5 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 13 Jul 2016 19:44:12 -0700 Subject: [PATCH 102/122] Initial commit of monitor.py script --- monitor.py | 222 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 monitor.py diff --git a/monitor.py b/monitor.py new file mode 100644 index 0000000..781c9db --- /dev/null +++ b/monitor.py @@ -0,0 +1,222 @@ +""" +Monitor for IPython/Jupyter console commands run from Vim. + +Usage: + 1. Run jupyter/ipython console + 2. Run python monitor.py + 3. Connect Vim to console kernel using IPython command +""" +from __future__ import print_function +import ast +import os +import re +import six +import sys +try: + from jupyter_client import KernelManager, find_connection_file +except ImportError: + from IPython.kernel import KernelManager, find_connection_file +try: + from Queue import Empty +except ImportError: + from queue import Empty +from glob import glob + +try: + from pygments import highlight +except ImportError: + highlight = lambda code, *args: code +else: + from pygments.lexers import PythonLexer, Python3Lexer + from pygments.formatters import TerminalFormatter + formatter = TerminalFormatter() + lexer = Python3Lexer() if six.PY3 else PythonLexer() + +colors = {k: i for i, k in enumerate([ + 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'])} + + +def paths(): + for fullpath in glob(os.path.join(os.path.dirname(filename), 'kernel*')): + if not re.match('^(.*/)?kernel-[0-9]+.json', fullpath): + continue + yield fullpath + + +connected = False +while not connected: + try: + filename = find_connection_file('kernel*') + except IOError: + continue + + for fullpath in paths(): + km = KernelManager(connection_file=fullpath) + km.load_connection_file() + + kc = km.client() + kc.start_channels() + try: + send = kc.execute + except AttributeError: + send = kc.shell_channel.execute + if not hasattr(kc, 'iopub_channel'): + kc.iopub_channel = kc.sub_channel + + send('', silent=True) + try: + msg = kc.shell_channel.get_msg(timeout=1) + connected = True + socket = km.connect_iopub() + print('IPython monitor connected successfully') + break + except KeyboardInterrupt: + sys.exit(0) + except (Empty, KeyError): + continue + except Exception as e: + import traceback + traceback.print_exc() + finally: + if not connected: + kc.stop_channels() + + +def colorize(string, color, bold=False, bright=False): + if isinstance(color, str): + code = ''.join(('\033[', str(colors[color] + (90 if bright else 30)))) + else: + code = '\033[38;5;%d' % color + return ''.join((code, ';1' if bold else '', 'm', string, '\033[0m')) + + +def get_msgs(): + try: + kc.iopub_channel.flush() + return kc.iopub_channel.get_msgs() + except AttributeError: + msgs = [] + while True: + try: + msgs.append(kc.iopub_channel.get_msg(timeout=0.001)) + except Empty: + return msgs + + +if len(sys.argv) > 1: + term = open(sys.argv[1], 'w') + sys.stdout = term +else: + msg_id = send('import os as _os; _tty = _os.ttyname(1)', silent=True, + user_expressions=dict(_tty='_tty')) + while True: + try: + msg = kc.shell_channel.get_msg(timeout=1.0) + if msg['parent_header']['msg_id'] == msg_id: + sys.stdout = open(ast.literal_eval( + msg['content']['user_expressions'] + ['_tty']['data']['text/plain']), 'w+') + break + except Empty: + continue + + +class IPythonMonitor(object): + + def __init__(self): + self.clients = set() + self.execution_count_id = None + self.last_msg_type = None # Only set when text written to stdout + self.last_execution_count = 0 + + def print_prompt(self, start='In', color=28, num_color=46, count_offset=0): + count = str(self.last_execution_count + count_offset) + sys.stdout.write(colorize(start.rstrip() + ' [', color)) + sys.stdout.write(colorize(count, num_color, bold=True)) + sys.stdout.write(colorize(']: ', color)) + return '%s [%s]: ' % (start.strip(), count) + + def listen(self): + while socket.recv(): + for msg in get_msgs(): + msg_type = msg['msg_type'] + + if msg_type == 'shutdown_reply': + sys.exit(0) + + client = msg['parent_header'].get('session', '') + if (client and msg_type in ('execute_input', 'pyin') and + msg['content']['code'] == '"_vim_client";_=_;__=__'): + self.clients.add(client) + continue + if client not in self.clients: + continue + + getattr(self, msg_type, self.other)(msg) + sys.stdout.flush() + + def pyin(self, msg): + self.last_execution_count = msg['content']['execution_count'] + sys.stdout.write('\r') + dots = ' ' * (len(self.print_prompt().rstrip()) - 1) + ': ' + code = highlight(msg['content']['code'], lexer, formatter) + output = code.rstrip().replace('\n', '\n' + colorize(dots, 28)) + sys.stdout.write(output) + self.execution_count_id = msg['parent_header']['msg_id'] + self.last_msg_type = msg['msg_type'] + + def pyout(self, msg, prompt=True, spaces=''): + if 'execution_count' in msg['content']: + self.last_execution_count = msg['content']['execution_count'] + self.execution_count_id = msg['parent_header']['msg_id'] + output = msg['content']['data']['text/plain'] + if prompt: + self.print_prompt('\nOut', 196, 196) + sys.stdout.write(('\n' if '\n' in output else '') + output) + else: + sys.stdout.write(output) + self.last_msg_type = msg['msg_type'] + + def display_data(self, msg): + sys.stdout.write('\n') + self.pyout(msg, prompt=False) + + def pyerr(self, msg): + for line in msg['content']['traceback']: + sys.stdout.write('\n' + line) + if self.last_msg_type not in ('execute_input', 'pyin'): + self.print_prompt('\nIn') + self.last_msg_type = msg['msg_type'] + + def stream(self, msg): + if self.last_msg_type not in ('pyerr', 'error', 'stream'): + sys.stdout.write('\n') + try: + data = msg['content']['data'] + except KeyError: + data = msg['content']['text'] + sys.stdout.write(colorize(data, 'cyan', bright=True)) + self.last_msg_type = msg['msg_type'] + + def status(self, msg): + if (msg['content']['execution_state'] == 'idle' and + msg['parent_header']['msg_id'] == self.execution_count_id): + self.print_prompt('\nIn', count_offset=1) + self.execution_count_id = None + + def clear_output(self, msg): + if self.last_msg_type in ('execute_input', 'pyin'): + print('\n') + print('\033[2K\r', file=sys.stdout, end='') + + def other(self, msg): + print('msg_type = %s' % str(msg['msg_type'])) + print('msg = %s' % str(msg)) + + execute_input = pyin + execute_result = pyout + error = pyerr + + +monitor = IPythonMonitor() +monitor.listen() From 953abc2afb8d372f0c31c77d59dcb127f5aa1966 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Wed, 13 Jul 2016 20:42:22 -0700 Subject: [PATCH 103/122] Rename metadata fields to match Vim naming --- ftplugin/python/ipy.vim | 14 +++++++------- ftplugin/python/vim_ipython.py | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index f58bd93..4548300 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -269,16 +269,16 @@ def process_matches(matches, metadata, result): for c, m in zip(completions, metadata): # vim can't handle null bytes in Python strings m = {k: v.replace('\0', '^@') for k, v in m.items()} - result.c, result.m = c, m + result.word = c if 'info' in m: - r.text, r.info = m['text'], m['info'] - vim.command('call add(res, {"word": IPythonPyeval("r.c"), ' - '"menu": IPythonPyeval("r.text"), ' + result.menu, result.info = m['menu'], m['info'] + vim.command('call add(res, {"word": IPythonPyeval("r.word"), ' + '"menu": IPythonPyeval("r.menu"), ' '"info": IPythonPyeval("r.info")})') else: - r.text = m.get('text', '') - vim.command('call add(res, {"word": IPythonPyeval("r.c"), ' - '"menu": IPythonPyeval("r.text")})') + result.menu = m.get('menu', '') + vim.command('call add(res, {"word": IPythonPyeval("r.word"), ' + '"menu": IPythonPyeval("r.menu")})') endpython fun! CompleteIPython(findstart, base) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index a95d206..26c1aa4 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -474,7 +474,7 @@ def get_completion_metadata(): else: metadata = content['user_expressions']['_completions'] metadata = ast.literal_eval(metadata['data']['text/plain']) - matches = [c['match'] for c in metadata] + matches = [c['word'] for c in metadata] return matches, metadata def vim_ipython_is_open(): From e5e9d59cc695cf2ad47ecc58c6944a3de242070c Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 14 Jul 2016 09:32:55 -0700 Subject: [PATCH 104/122] Disallow stdin until input_requests are handled Currently stdin_channel is ignored which means the kernel freezes waiting for a stdin reply if input() is called. Pass allow_stdin=False to give an immediate exception instead of hanging. --- ftplugin/python/vim_ipython.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 26c1aa4..5dc2558 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -229,6 +229,7 @@ def km_from_string(s=''): def send(msg, **kwargs): kwds = dict( store_history=vim_vars.get('ipython_store_history', True), + allow_stdin=False, ) kwds.update(kwargs) return execute(msg, **kwds) From ed4c2dc302718712db0df2e6634068c284a6df55 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 14 Jul 2016 09:41:20 -0700 Subject: [PATCH 105/122] Add documentation for metadata and monitor --- doc/ipython.txt | 116 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 doc/ipython.txt diff --git a/doc/ipython.txt b/doc/ipython.txt new file mode 100644 index 0000000..dff100a --- /dev/null +++ b/doc/ipython.txt @@ -0,0 +1,116 @@ +*ipython.txt* + +============================================================================== +Contents *vim-ipython-contents* + +1. Completion Metadata |vim-ipython-metadata| +2. IPython Monitor |vim-ipython-monitor| +3. Variables |vim-ipython-variables| + 3.1. g:ipy_autostart |g:ipy_autostart| + 3.2. g:ipy_completefunc |g:ipy_completefunc| + 3.3. g:ipy_input |g:ipy_input| + 3.4. g:ipy_perform_mappings |g:ipy_perform_mappings| + 3.5. g:ipython_completion_timeout |g:ipython_completion_timeout| + 3.6. g:ipython_dictionary_completion |g:ipython_dictionary_completion| + 3.7. g:ipython_greedy_matching |g:ipython_greedy_matching| + 3.8. g:ipython_history_len |g:ipython_history_len| + 3.9. g:ipython_history_raw |g:ipython_history_raw| + 3.10. g:ipython_history_timeout |g:ipython_history_timeout| + 3.11. g:ipython_history_unique |g:ipython_history_unique| + 3.12. g:ipython_run_flags |g:ipython_run_flags| + 3.13. g:ipython_store_history |g:ipython_store_history| + 3.14. g:ipython_timeout |g:ipython_timeout| + +============================================================================== +1. Completion Metadata *vim-ipython-metadata* + +vim-ipython supports user-supplied metadata associated with completions from +the IPython shell. The plugin fetches the metadata from the IPython kernel +using the user-defined function `completion_metadata` which takes one +parameter - the result of `get_ipython()`. If the function does not exist in +IPython's namespace, completion will still work but without any menu/info +entries in the completion menu. Each completion match should have a +corresponding metadata dictionary with "word", "menu", and "info" fields. A +basic (and slow) implementation of such a function follows: > + + def completion_metadata(ip): + import inspect + import six + metadata = [dict(word=m) for m in ip.Completer.matches] + for m in metadata: + try: + obj = eval(m['word'], ip.user_ns) + except Exception: + continue + m['menu'] = six.moves.reprlib.repr(obj) + info = inspect.getdoc(obj) + if info: + m['info'] = info + return metadata + +============================================================================== +2. IPython Monitor *vim-ipython-monitor* + +The included `monitor.py` script listens in on message from Vim to the IPython +kernel to echo inputs and outputs to the kernel in real-time. The script must +be started before connecting Vim to the IPython kernel so that it can +differentiate between Vim and the Jupyter shell clients. The `:IPython` +command can be executed multiple times without ill effect in case the monitor +is started later on. + +Basic usage: > + + $ python monitor.py &; jupyter console + :IPython + +Note: Currently the script looks for a connection file automatically and will +connect to the first connection file it finds matching the glob pattern +"kernel-[0-9]*.json'. This means the script will not connect to IPython +notebook kernels by design. + +============================================================================== +3. Variables *vim-ipython-variables* + +------------------------------------------------------------------------------ +3.1. `g:ipy_autostart` *g:ipy_autostart* + +------------------------------------------------------------------------------ +3.2. `g:ipy_completefunc` *g:ipy_completefunc* + +------------------------------------------------------------------------------ +3.3. `g:ipy_input` *g:ipy_input* + +------------------------------------------------------------------------------ +3.4. `g:ipy_perform_mappings` *g:ipy_perform_mappings* + +------------------------------------------------------------------------------ +3.5. `g:ipython_completion_timeout` *g:ipython_completion_timeout* + +------------------------------------------------------------------------------ +3.6. `g:ipython_dictionary_completion` *g:ipython_dictionary_completion* + +------------------------------------------------------------------------------ +3.7. `g:ipython_greedy_matching` *g:ipython_greedy_matching* + +------------------------------------------------------------------------------ +3.8. `g:ipython_history_len` *g:ipython_history_len* + +------------------------------------------------------------------------------ +3.9. `g:ipython_history_raw` *g:ipython_history_raw* + +------------------------------------------------------------------------------ +3.10. `g:ipython_history_timeout` *g:ipython_history_timeout* + +------------------------------------------------------------------------------ +3.11. `g:ipython_history_unique` *g:ipython_history_unique* + +------------------------------------------------------------------------------ +3.12. `g:ipython_run_flags` *g:ipython_run_flags* + +------------------------------------------------------------------------------ +3.13. `g:ipython_store_history` *g:ipython_store_history* + +------------------------------------------------------------------------------ +3.14. `g:ipython_timeout` *g:ipython_timeout* + + vim: textwidth=78 et filetype=help:norightleft: From 0437e45d5fa650a224e376514b30477db4a1d2f1 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 15 Jul 2016 13:06:28 -0700 Subject: [PATCH 106/122] Add history Unite source --- autoload/unite/sources/history_ipython.vim | 197 +++++++++++++++++++++ doc/ipython.txt | 103 +++++++---- 2 files changed, 270 insertions(+), 30 deletions(-) create mode 100644 autoload/unite/sources/history_ipython.vim diff --git a/autoload/unite/sources/history_ipython.vim b/autoload/unite/sources/history_ipython.vim new file mode 100644 index 0000000..39edb0f --- /dev/null +++ b/autoload/unite/sources/history_ipython.vim @@ -0,0 +1,197 @@ +if exists('g:loaded_history_ipython') + finish +endif +let g:loaded_history_ipython = 1 + +let s:save_cpo = &cpo +set cpo&vim + +function! unite#sources#history_ipython#define() + return s:source +endfunction + +let s:source = { + \ 'name' : 'history/ipython', + \ 'description' : 'candidates from IPython history', + \ 'action_table' : {}, + \ 'hooks' : {}, + \ 'default_action' : 'send', + \ 'default_kind' : 'word', + \ 'syntax' : 'uniteSource__Python', + \ 'max_candidates' : 100, + \} + +function! s:source.hooks.on_syntax(args, context) + let save_current_syntax = get(b:, 'current_syntax', '') + unlet! b:current_syntax + + try + silent! syntax include @Python syntax/python.vim + syntax region uniteSource__IPythonPython + \ start=' ' end='$' contains=@Python containedin=uniteSource__IPython + let &l:iskeyword = substitute(&l:iskeyword, ',!$\|!,', '', '') + finally + let b:current_syntax = save_current_syntax + endtry +endfunction + +function! s:source.hooks.on_init(args, context) + if !exists('*IPythonHistory') + call unite#print_source_error( + \ 'IPythonHistory() does not exist', s:source.name) + return + endif + + let args = unite#helper#parse_source_args(a:args) + let a:context.source__session = get(a:context, 'source__session', -1) + if a:context.source__session == -1 + let a:context.source__session = get(args, 0, -1) + endif + let a:context.source__input = a:context.input + if a:context.source__input == '' || a:context.unite__is_restart + let a:context.source__input = unite#util#input('Pattern: ', + \ a:context.source__input, + \ 'customlist,IPythonCmdComplete') + endif + + call unite#print_source_message('Pattern: ' + \ . a:context.source__input, s:source.name) +endfunction + +function! s:source.gather_candidates(args, context) + if !exists('*IPythonHistory') + return [] + endif + + return map(IPythonHistory(a:context.source__input, + \ a:context.source__session), '{ + \ "word" : v:val.code, + \ "abbr" : printf("'''''' %d/%d '''''' %s", v:val.session, v:val.line, + \ v:val.code =~ "\n" ? + \ "\n" . join(split(v:val.code, "\n")[:50], "\n") : v:val.code), + \ "is_multiline" : 1, + \ "source__session" : v:val.session, + \ "source__line" : v:val.line, + \ "source__context" : a:context, + \ "action__regtype" : "V", + \ }') +endfunction + +let s:source.action_table.send = { + \ 'description' : 'run in IPython', + \ 'is_selectable' : 1, + \ } +function! s:source.action_table.send.func(candidates) + for candidate in a:candidates + let g:ipy_input = candidate.word + Python2or3 run_ipy_input() + silent! unlet g:ipy_input + endfor +endfunction + +let s:source.action_table.session = { + \ 'description' : "get history for candidate's session", + \ 'is_quit' : 0, + \ 'is_invalidate_cache' : 1, + \ } +function! s:source.action_table.session.func(candidate) + let context = a:candidate.source__context + let context.source__input = unite#util#input('Pattern: ', + \ context.source__input, + \ 'customlist,IPythonCmdComplete') + let context.source__session = a:candidate.source__session +endfunction + +let s:source.action_table.session_info = { + \ 'description' : "print information about a session", + \ 'is_quit' : 0, + \ } +function! s:source.action_table.session_info.func(candidate) + let store_history = get(g:, 'ipython_store_history', 1) + try + let g:ipython_store_history = 0 + let session_info = [ + \ "from IPython import get_ipython", + \ "def _session_info(session=0):", + \ " def date(d):", + \ " return d.strftime('%a %d%b%Y %T')", + \ " session_id, start, end, cmds, remark = " . + \ " get_ipython().history_manager.get_session_info(session)", + \ " val = 'start: {0}'.format(date(start))", + \ " if end:", + \ " val += '; end: {0}; {1} commands'.format(date(end), cmds)", + \ " return val", + \ ] + let g:ipy_input = join(session_info, "\n") + silent Python2or3 run_ipy_input(silent=True) + let g:ipy_input = printf('_session_info(%d)', a:candidate.source__session) + silent! unlet g:ipy_result + Python2or3 eval_ipy_input('g:ipy_result') + echomsg printf('session %d: %s', + \ a:candidate.source__session, g:ipy_result) + finally + let g:ipython_store_history = store_history + endtry +endfunction + +let s:source.action_table.macro = { + \ 'description' : 'create IPython macro', + \ 'is_selectable' : 1, + \ } +function! s:source.action_table.macro.func(candidates) + let g:ipy_input = printf('%%macro %s %s', + \ unite#util#input('Macro name: '), + \ join(map(a:candidates, + \ 'printf("%s/%s", v:val.source__session, v:val.source__line)')) + \ ) + Python2or3 run_ipy_input() + silent! unlet g:ipy_input +endfunction + +let s:source.action_table.yank = { + \ 'description' : 'yank candidates', + \ 'is_selectable' : 1, + \ 'is_quit' : 1, + \ } +function! s:source.action_table.yank.func(candidates) + if len(a:candidates) == 1 && a:candidates[0].word !~ "\n" + let text = a:candidates[0].word + let mode = 'v' + else + let text = join(map(copy(a:candidates), 'v:val.word'), "\n\n") + let mode = 'V' + endif + call setreg('"', text, mode) + if has('clipboard') + if &clipboard =~# '\' + call setreg('*', text, mode) + endif + if &clipboard =~# '\' + call setreg('+', text, mode) + endif + endif + + echohl Question | echo 'Yanked:' | echohl Normal + echo text +endfunction + +let s:source.action_table.append = { + \ 'description' : 'append candidates', + \ 'is_selectable' : 1, + \ } +function! s:source.action_table.append.func(candidates) + put = join(map(copy(a:candidates), 'v:val.word'), \"\n\n\") +endfunction + +let s:source.action_table.insert = { + \ 'description' : 'insert candidates', + \ 'is_selectable' : 1, + \ } +function! s:source.action_table.insert.func(candidates) + put! = join(map(copy(a:candidates), 'v:val.word'), \"\n\n\") +endfunction + +let &cpo = s:save_cpo +unlet s:save_cpo + +" vim:set et ts=2 sts=2 sw=2: diff --git a/doc/ipython.txt b/doc/ipython.txt index dff100a..a664ac1 100644 --- a/doc/ipython.txt +++ b/doc/ipython.txt @@ -5,21 +5,22 @@ Contents *vim-ipython-contents* 1. Completion Metadata |vim-ipython-metadata| 2. IPython Monitor |vim-ipython-monitor| -3. Variables |vim-ipython-variables| - 3.1. g:ipy_autostart |g:ipy_autostart| - 3.2. g:ipy_completefunc |g:ipy_completefunc| - 3.3. g:ipy_input |g:ipy_input| - 3.4. g:ipy_perform_mappings |g:ipy_perform_mappings| - 3.5. g:ipython_completion_timeout |g:ipython_completion_timeout| - 3.6. g:ipython_dictionary_completion |g:ipython_dictionary_completion| - 3.7. g:ipython_greedy_matching |g:ipython_greedy_matching| - 3.8. g:ipython_history_len |g:ipython_history_len| - 3.9. g:ipython_history_raw |g:ipython_history_raw| - 3.10. g:ipython_history_timeout |g:ipython_history_timeout| - 3.11. g:ipython_history_unique |g:ipython_history_unique| - 3.12. g:ipython_run_flags |g:ipython_run_flags| - 3.13. g:ipython_store_history |g:ipython_store_history| - 3.14. g:ipython_timeout |g:ipython_timeout| +3. IPython History Unite Source |vim-ipython-history| +4. Variables |vim-ipython-variables| + 4.1. g:ipy_autostart |g:ipy_autostart| + 4.2. g:ipy_completefunc |g:ipy_completefunc| + 4.3. g:ipy_input |g:ipy_input| + 4.4. g:ipy_perform_mappings |g:ipy_perform_mappings| + 4.5. g:ipython_completion_timeout |g:ipython_completion_timeout| + 4.6. g:ipython_dictionary_completion |g:ipython_dictionary_completion| + 4.7. g:ipython_greedy_matching |g:ipython_greedy_matching| + 4.8. g:ipython_history_len |g:ipython_history_len| + 4.9. g:ipython_history_raw |g:ipython_history_raw| + 4.10. g:ipython_history_timeout |g:ipython_history_timeout| + 4.11. g:ipython_history_unique |g:ipython_history_unique| + 4.12. g:ipython_run_flags |g:ipython_run_flags| + 4.13. g:ipython_store_history |g:ipython_store_history| + 4.14. g:ipython_timeout |g:ipython_timeout| ============================================================================== 1. Completion Metadata *vim-ipython-metadata* @@ -69,48 +70,90 @@ connect to the first connection file it finds matching the glob pattern notebook kernels by design. ============================================================================== -3. Variables *vim-ipython-variables* +3. IPython History Unite Source *vim-ipython-history* + +Note: Requires unite.vim: https://github.com/Shougo/unite.vim + +The plugin includes a Unite source named "history/ipython" providing an +interface to the history messaging in IPython. The source will prompt for a +glob pattern to search for. If no pattern is provided, the search results in +up to |g:ipython_history_len| of the most recent IPython commands. If the +pattern begins or ends with a '*', the other end of the pattern is anchored at +the start or end of the match. For example, > + + Pattern: def * + +will return results that start with a function definition and > + + Pattern: *) + +will return results ending with ')'. Otherwise a '*' is both prepended +and appended to the pattern, so > + + Pattern: sys + +will return results containing "sys" anywhere. + +The input prompt allows completion from the IPython namespace. + +After selecting a history entry, the available actions are (in addition to +Unite's common actions): + + - `append` (insert the entry after the cursor line) + - `insert` (insert the entry before the cursor line) + - `macro` (prompt for a macro name and create an IPython macro to repeat + the commands) + - `send` (repeat the command in IPython) + - `session` (restart the history/ipython source showing entries only from + the same session as the selected entry) + - `session_info` (print session date and start time) + - `yank` (yank entries to unnamed and clipboard if 'clipboard' is set) + +Multiple history entries may be selected for all of the actions. + +============================================================================== +4. Variables *vim-ipython-variables* ------------------------------------------------------------------------------ -3.1. `g:ipy_autostart` *g:ipy_autostart* +4.1. `g:ipy_autostart` *g:ipy_autostart* ------------------------------------------------------------------------------ -3.2. `g:ipy_completefunc` *g:ipy_completefunc* +4.2. `g:ipy_completefunc` *g:ipy_completefunc* ------------------------------------------------------------------------------ -3.3. `g:ipy_input` *g:ipy_input* +4.3. `g:ipy_input` *g:ipy_input* ------------------------------------------------------------------------------ -3.4. `g:ipy_perform_mappings` *g:ipy_perform_mappings* +4.4. `g:ipy_perform_mappings` *g:ipy_perform_mappings* ------------------------------------------------------------------------------ -3.5. `g:ipython_completion_timeout` *g:ipython_completion_timeout* +4.5. `g:ipython_completion_timeout` *g:ipython_completion_timeout* ------------------------------------------------------------------------------ -3.6. `g:ipython_dictionary_completion` *g:ipython_dictionary_completion* +4.6. `g:ipython_dictionary_completion` *g:ipython_dictionary_completion* ------------------------------------------------------------------------------ -3.7. `g:ipython_greedy_matching` *g:ipython_greedy_matching* +4.7. `g:ipython_greedy_matching` *g:ipython_greedy_matching* ------------------------------------------------------------------------------ -3.8. `g:ipython_history_len` *g:ipython_history_len* +4.8. `g:ipython_history_len` *g:ipython_history_len* ------------------------------------------------------------------------------ -3.9. `g:ipython_history_raw` *g:ipython_history_raw* +4.9. `g:ipython_history_raw` *g:ipython_history_raw* ------------------------------------------------------------------------------ -3.10. `g:ipython_history_timeout` *g:ipython_history_timeout* +4.10. `g:ipython_history_timeout` *g:ipython_history_timeout* ------------------------------------------------------------------------------ -3.11. `g:ipython_history_unique` *g:ipython_history_unique* +4.11. `g:ipython_history_unique` *g:ipython_history_unique* ------------------------------------------------------------------------------ -3.12. `g:ipython_run_flags` *g:ipython_run_flags* +4.12. `g:ipython_run_flags` *g:ipython_run_flags* ------------------------------------------------------------------------------ -3.13. `g:ipython_store_history` *g:ipython_store_history* +4.13. `g:ipython_store_history` *g:ipython_store_history* ------------------------------------------------------------------------------ -3.14. `g:ipython_timeout` *g:ipython_timeout* +4.14. `g:ipython_timeout` *g:ipython_timeout* vim: textwidth=78 et filetype=help:norightleft: From 4dc32dd0b33b7577f05242171f760a54b2ba32fa Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 15 Jul 2016 15:13:19 -0700 Subject: [PATCH 107/122] Fix run_these_lines() maps --- ftplugin/python/ipy.vim | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 4548300..d9852d3 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -149,7 +149,13 @@ augroup END noremap (IPython-RunFile) :update:Python2or3 run_this_file() noremap (IPython-ImportFile) :update:Python2or3 run_this_file('-n') noremap (IPython-RunLine) :Python2or3 run_this_line() -noremap (IPython-RunLines) :Python2or3 run_these_lines() +if has('python3') && get(g:, 'pymode_python', '') !=# 'python' + noremap (IPython-RunLines) :python3 run_these_lines() + xnoremap (IPython-RunLinesAsTopLevel) :python3 dedent_run_these_lines() +else + noremap (IPython-RunLines) :python run_these_lines() + xnoremap (IPython-RunLinesAsTopLevel) :python dedent_run_these_lines() +endif noremap (IPython-OpenPyDoc) :Python2or3 get_doc_buffer() noremap (IPython-UpdateShell) :Python2or3 if update_subchannel_msgs(force=True): echo("vim-ipython shell updated",'Operator') noremap (IPython-ToggleReselect) :Python2or3 toggle_reselect() @@ -162,7 +168,6 @@ noremap (IPython-ToggleSendOnSave) :call toggle_send_on_save() noremap (IPython-PlotClearCurrent) :Python2or3 run_command("plt.clf()") noremap (IPython-PlotCloseAll) :Python2or3 run_command("plt.close('all')") noremap (IPython-RunLineAsTopLevel) :Python2or3 dedent_run_this_line() -xnoremap (IPython-RunLinesAsTopLevel) :Python2or3 dedent_run_these_lines() function! s:DoMappings() let b:did_ipython = 1 From 773b067937887bf60cf665fac28a2996e8d51e4c Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Mon, 18 Jul 2016 18:59:12 -0700 Subject: [PATCH 108/122] Add RunCell implementation (run paragraph) --- ftplugin/python/ipy.vim | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index d9852d3..61d7498 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -168,6 +168,8 @@ noremap (IPython-ToggleSendOnSave) :call toggle_send_on_save() noremap (IPython-PlotClearCurrent) :Python2or3 run_command("plt.clf()") noremap (IPython-PlotCloseAll) :Python2or3 run_command("plt.close('all')") noremap (IPython-RunLineAsTopLevel) :Python2or3 dedent_run_this_line() +noremap (IPython-RunTextObj) :set opfunc=opfuncg@ +noremap (IPython-RunCell) :set opfunc=opfuncg@ap function! s:DoMappings() let b:did_ipython = 1 @@ -177,6 +179,7 @@ function! s:DoMappings() map g (IPython-ImportFile) endif " map (IPython-RunLine) + map (IPython-RunTextObj) map (IPython-RunLines) map ,d (IPython-OpenPyDoc) map (IPython-UpdateShell) @@ -434,3 +437,43 @@ function! GreedyCompleteIPython(findstart, base) return IPythonCmdComplete(a:base, a:base, len(a:base), 1) endif endfunction + +function! s:opfunc(type) + " Originally from tpope/vim-scriptease + let sel_save = &selection + let cb_save = &clipboard + let reg_save = @@ + let left_save = getpos("'<") + let right_save = getpos("'>") + let vimode_save = visualmode() + try + set selection=inclusive clipboard-=unnamed clipboard-=unnamedplus + if a:type =~ '^\d\+$' + silent exe 'normal! ^v'.a:type.'$hy' + elseif a:type =~# '^.$' + silent exe "normal! `<" . a:type . "`>y" + elseif a:type ==# 'line' + silent exe "normal! '[V']y" + elseif a:type ==# 'block' + silent exe "normal! `[\`]y" + elseif a:type ==# 'visual' + silent exe "normal! gvy" + else + silent exe "normal! `[v`]y" + endif + redraw + let l:cmd = @@ + finally + let @@ = reg_save + let &selection = sel_save + let &clipboard = cb_save + exe "normal! " . vimode_save . "\" + call setpos("'<", left_save) + call setpos("'>", right_save) + endtry +Python2or3 << EOF +import textwrap +import vim +run_command(textwrap.dedent(vim.eval('l:cmd'))) +EOF +endfunction From 9dd97ecb2acec9557c7350eacdda9d02f5f40ba0 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sat, 5 Nov 2016 19:39:33 -0700 Subject: [PATCH 109/122] Replace Result class with dict in process_matches This prevents having to check if the metadata dict contains the 'info' or 'menu' keys and allows other keys e.g. 'abbr' to be used automatically if they are specified in the completion_metadata function. --- ftplugin/python/ipy.vim | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 61d7498..eb732b7 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -96,8 +96,6 @@ vim_ipython_path = vim.eval("expand(':h')") sys.path.append(vim_ipython_path) from vim_ipython import * -class Result(object): - pass endpython fun! toggle_send_on_save() @@ -262,8 +260,6 @@ def process_matches(matches, metadata, result): completions = matches else: completions = [s.encode(vim_encoding) for s in matches] - for i, m in enumerate(metadata): - metadata[i] = {k: v.encode(vim_encoding) for k, v in m.items()} if vim.vars['ipython_dictionary_completion'] and not vim.vars['ipython_greedy_matching']: for char in '\'"': if any(c.endswith(char + ']') for c in completions): @@ -277,16 +273,11 @@ def process_matches(matches, metadata, result): for c, m in zip(completions, metadata): # vim can't handle null bytes in Python strings m = {k: v.replace('\0', '^@') for k, v in m.items()} - result.word = c - if 'info' in m: - result.menu, result.info = m['menu'], m['info'] - vim.command('call add(res, {"word": IPythonPyeval("r.word"), ' - '"menu": IPythonPyeval("r.menu"), ' - '"info": IPythonPyeval("r.info")})') - else: - result.menu = m.get('menu', '') - vim.command('call add(res, {"word": IPythonPyeval("r.word"), ' - '"menu": IPythonPyeval("r.menu")})') + result.clear() + result.update(m) + vim.command('call add(res, {%s})' % ','.join( + '"{k}": IPythonPyeval("r[\'{k}\']")'.format(k=k) + for k in result)) endpython fun! CompleteIPython(findstart, base) @@ -354,7 +345,7 @@ except IOError: else: vim.command('setlocal omnifunc=') vim.command('return []') -r = Result() # result object to let vim access namespace while in a function +r = dict() # result object to let vim access namespace while in a function process_matches(matches, metadata, r) endpython return res @@ -415,7 +406,7 @@ if ' ' in arglead: arglead = arglead.rpartition(' ')[0] matches = ['%s %s' % (arglead, m) for m in matches] if int(vim.eval('a:0')): - r = Result() + r = dict() process_matches(matches, metadata, r) endpython if a:0 From 1f9d1da6a79fe5383be740c7d037b83941245feb Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Mon, 7 Nov 2016 19:07:55 -0700 Subject: [PATCH 110/122] Don't cause an exception if metadata doesn't exist Don't cause an exception in the user's IPython if they haven't defined the completion_metadata function. --- ftplugin/python/vim_ipython.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 5dc2558..fa57513 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -459,7 +459,12 @@ def ipy_complete(base, current_line, pos): def get_completion_metadata(): """Generate and fetch completion metadata.""" - request = '_completions = completion_metadata(get_ipython())' + request = ''' +try: + _completions = completion_metadata(get_ipython()) +except Exception: + pass +''' try: msg_id = send(request, silent=True, user_variables=['_completions']) except TypeError: # change in IPython 3.0+ From a66b4a1e033204d774af29ba031e2329ccd13890 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Mon, 7 Nov 2016 19:10:04 -0700 Subject: [PATCH 111/122] Don't require 'word' in the metadata dict Accidentally didn't put back 'word' into the result. This is a big problem if the user's completion_metadata doesn't manually add 'word' to the dict. Oops. Intentionally wrote it so the user may override 'word' in the metadata dict if desired (e.g. to add an opening parenthesis to a function call). --- ftplugin/python/ipy.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index eb732b7..f4eb17d 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -272,9 +272,9 @@ def process_matches(matches, metadata, result): pass for c, m in zip(completions, metadata): # vim can't handle null bytes in Python strings - m = {k: v.replace('\0', '^@') for k, v in m.items()} result.clear() - result.update(m) + result['word'] = c + result.update({k: v.replace('\0', '^@') for k, v in m.items()}) vim.command('call add(res, {%s})' % ','.join( '"{k}": IPythonPyeval("r[\'{k}\']")'.format(k=k) for k in result)) From d07dd0a1ab07f607fabb89d896faa19f6cfb16bb Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Mon, 7 Nov 2016 19:18:09 -0700 Subject: [PATCH 112/122] Move comment to the right place --- ftplugin/python/ipy.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index f4eb17d..fa00842 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -271,9 +271,9 @@ def process_matches(matches, metadata, result): except ValueError: pass for c, m in zip(completions, metadata): - # vim can't handle null bytes in Python strings result.clear() result['word'] = c + # vim can't handle null bytes in Python strings result.update({k: v.replace('\0', '^@') for k, v in m.items()}) vim.command('call add(res, {%s})' % ','.join( '"{k}": IPythonPyeval("r[\'{k}\']")'.format(k=k) From ba8d8fea59b3e89602ba2cc4b934f872535cf896 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 27 Jan 2017 20:48:18 -0700 Subject: [PATCH 113/122] Don't use dictionary comprehension --- ftplugin/python/ipy.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index fa00842..8ba8b1f 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -274,7 +274,8 @@ def process_matches(matches, metadata, result): result.clear() result['word'] = c # vim can't handle null bytes in Python strings - result.update({k: v.replace('\0', '^@') for k, v in m.items()}) + for k, v in m.items(): + result[k] = v.replace('\0', '^@') vim.command('call add(res, {%s})' % ','.join( '"{k}": IPythonPyeval("r[\'{k}\']")'.format(k=k) for k in result)) From eb566c8e290bcb646a193b48b36a3564bbfd2ff6 Mon Sep 17 00:00:00 2001 From: Andreas Weh Date: Wed, 15 Mar 2017 11:47:21 +0100 Subject: [PATCH 114/122] Make doc buffer `nomodifiable`. --- ftplugin/python/vim_ipython.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index fa57513..4d5ab72 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -407,7 +407,7 @@ def get_doc_buffer(level=0, word=None): b = vim.current.buffer b[:] = None b[:] = doc - vim.command('setlocal nomodified bufhidden=wipe') + vim.command('setlocal nomodified bufhidden=wipe nomodifiable readonly nospell') #vim.command('setlocal previewwindow nomodifiable nomodified ro') #vim.command('set previewheight=%d'%len(b))# go to previous window vim.command('resize %d'%len(b)) From 90350ebc965d004321301bc58f1d568051481173 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 6 Aug 2017 15:43:33 -0700 Subject: [PATCH 115/122] Disable mapping --- ftplugin/python/ipy.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 8ba8b1f..25a5def 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -179,7 +179,7 @@ function! s:DoMappings() " map (IPython-RunLine) map (IPython-RunTextObj) map (IPython-RunLines) - map ,d (IPython-OpenPyDoc) + "map ,d (IPython-OpenPyDoc) map (IPython-UpdateShell) map (IPython-ToggleReselect) "map (IPython-StartDebugging) From 6e02769dd81ebd85b39a7afb480d155aac672d88 Mon Sep 17 00:00:00 2001 From: jvalenzuela Date: Mon, 21 Aug 2017 15:30:37 +0100 Subject: [PATCH 116/122] add code to allow answers to stdin requests by editing the last line of the vim-ipython buffer --- ftplugin/python/vim_ipython.py | 63 ++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 4d5ab72..541887a 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -2,6 +2,8 @@ show_execution_count = False # wait to get numbers for In[43]: feedback? monitor_subchannel = False # update vim-ipython 'shell' on every send? current_line = '' +allow_stdin = True # whether or not to accept stdin requests +current_stdin_prompt = {} try: from queue import Empty # python3 convention @@ -229,7 +231,7 @@ def km_from_string(s=''): def send(msg, **kwargs): kwds = dict( store_history=vim_vars.get('ipython_store_history', True), - allow_stdin=False, + allow_stdin=allow_stdin, ) kwds.update(kwargs) return execute(msg, **kwds) @@ -502,6 +504,10 @@ def update_subchannel_msgs(debug=False, force=False): if kc is None or (not vim_ipython_is_open() and not force): return False msgs = kc.iopub_channel.get_msgs() + if allow_stdin: + msgs += kc.stdin_channel.get_msgs() + + global current_stdin_prompt b = vim.current.buffer startedin_vimipython = vim.eval('@%')=='vim-ipython' nwindows = len(vim.windows) @@ -556,6 +562,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 ') + current_stdin_prompt.clear() s = '' if 'msg_type' not in m['header']: # debug information @@ -594,6 +603,13 @@ def update_subchannel_msgs(debug=False, force=False): elif header == 'pyerr' or header == 'error': c = m['content'] s = "\n".join(map(strip_color_escapes,c['traceback'])) + 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 :py EnteredInsertMode()') + echo('Awaiting input. Answer by editing last vim-ipython line') if s.find('\n') == -1: # somewhat ugly unicode workaround from @@ -608,11 +624,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 len(vim.windows) > nwindows: pwin = int(vim.current.window.number) if pwin <= previouswin: @@ -649,7 +668,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) + # if the kernel it's waiting for input it's normal to get no reply + if not kc.stdin_channel.msg_ready(): + echo("In[]: %s (no reply from IPython kernel)" % prompt) else: echo("In[]: %s" % prompt) @@ -747,6 +768,42 @@ def run_these_lines(dedent=False): prompt = "lines %d-%d "% (r.start+1,r.end+1) print_prompt(prompt,msg_id) +@with_subchannel +def EnteredInsertMode(): + # If there is a pending input and we are in the last line + if current_stdin_prompt and vim.eval('line(".")')==vim.eval('line("$")'): + # save the current prompt, ask for input and restore the prompt + vim.command('call inputsave()') + input_call = ( + "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_call) + vim.command('call inputrestore()') + + # if the user replied to the input request + 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 :normal! G$') + vim.command('autocmd InsertLeave :autocmd! InsertLeave ') + vim.command('call feedkeys("\\", "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(): """ From 4e931ecde0d40f6c887319c6a1022c6c3483c0ef Mon Sep 17 00:00:00 2001 From: jvalenzuela Date: Sun, 27 Aug 2017 16:15:00 +0100 Subject: [PATCH 117/122] Added IPythonInput and IPythonInputSecret commands --- ftplugin/python/ipy.vim | 2 + ftplugin/python/vim_ipython.py | 77 ++++++++++++++++++++++++++-------- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/ftplugin/python/ipy.vim b/ftplugin/python/ipy.vim index 25a5def..d73da1e 100644 --- a/ftplugin/python/ipy.vim +++ b/ftplugin/python/ipy.vim @@ -238,6 +238,8 @@ command! -nargs=0 IPythonXSelection :Python2or3 km_from_string(vim.eval('@*')) command! -nargs=* IPythonNew :Python2or3 new_ipy("") command! -nargs=* IPythonInterrupt :Python2or3 interrupt_kernel_hack("") command! -nargs=0 IPythonTerminate :Python2or3 terminate_kernel_hack() +command! -nargs=0 -bang IPythonInput :Python2or3 InputPrompt(force='') +command! -nargs=0 -bang IPythonInputSecret :Python2or3 InputPrompt(force='', hide_input=True) function! IPythonBalloonExpr() Python2or3 << endpython diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 541887a..33283b6 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -609,7 +609,7 @@ def update_subchannel_msgs(debug=False, force=False): current_stdin_prompt['parent_msg_id'] = m['parent_header']['msg_id'] s += m['content']['prompt'] vim.command('autocmd InsertEnter :py EnteredInsertMode()') - echo('Awaiting input. Answer by editing last vim-ipython line') + echo('Awaiting input. call :IPythonInput or edit last vim-ipython line') if s.find('\n') == -1: # somewhat ugly unicode workaround from @@ -769,9 +769,23 @@ def run_these_lines(dedent=False): print_prompt(prompt,msg_id) @with_subchannel -def EnteredInsertMode(): - # If there is a pending input and we are in the last line - if current_stdin_prompt and vim.eval('line(".")')==vim.eval('line("$")'): +def InputPrompt(force=False, hide_input=False): + msgs = kc.stdin_channel.get_msgs() + for m in msgs: + global current_stdin_prompt + if 'msg_type' not in m['header']: + continue + current_stdin_prompt.clear() + header = m['header']['msg_type'] + if 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'] + + if not hide_input: + hide_input = current_stdin_prompt.get('is_password', False) + # If there is a pending input or we are forcing the input prompt + if (current_stdin_prompt or force) and kc: # save the current prompt, ask for input and restore the prompt vim.command('call inputsave()') input_call = ( @@ -780,8 +794,8 @@ def EnteredInsertMode(): "|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']) + ).format(input_command='inputsecret' if hide_input else 'input', + prompt=current_stdin_prompt.get('prompt', '')) vim.command(input_call) vim.command('call inputrestore()') @@ -789,20 +803,40 @@ def EnteredInsertMode(): 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 :normal! G$') - vim.command('autocmd InsertLeave :autocmd! InsertLeave ') - vim.command('call feedkeys("\\", "n")') + # write the reply to the vim-ipython buffer if it's not a password + if not hide_input and vim_ipython_is_open(): + + currentwin = int(vim.eval('winnr()')) + previouswin = int(vim.eval('winnr("#")')) + vim.command( + "try" + "|silent! wincmd P" + "|catch /^Vim\%((\a\+)\)\=:E441/" + "|endtry") + + if vim.eval('@%')=='vim-ipython': + b = vim.current.buffer + last_line = b[-1] + del b[-1] + b.append((last_line+reply).splitlines()) + vim.command(str(previouswin) + 'wincmd w') + vim.command(str(currentwin) + 'wincmd w') + kc.input(reply) - try: - child = get_child_msg(current_stdin_prompt['parent_msg_id']) - except Empty: - pass - current_stdin_prompt.clear() + if current_stdin_prompt: + try: + child = get_child_msg(current_stdin_prompt['parent_msg_id']) + except Empty: + pass + current_stdin_prompt.clear() + return True + else: + if not current_stdin_prompt: + echo('no input request detected') + if not kc: + echo('not connected to IPython') + return False def set_pid(): @@ -1000,3 +1034,10 @@ def get_session_history(session=None, pattern=None): return [] except KeyError: return [] + +def EnteredInsertMode(): + if current_stdin_prompt and vim.eval('line(".")')==vim.eval('line("$")'): + if InputPrompt(): + vim.command('autocmd InsertLeave :normal! G$') + vim.command('autocmd InsertLeave :autocmd! InsertLeave ') + vim.command('call feedkeys("\\", "n")') From a1954172544840fe84ea36f0ea477e9fa7ae1543 Mon Sep 17 00:00:00 2001 From: jvalenzuela Date: Sun, 27 Aug 2017 19:50:37 +0100 Subject: [PATCH 118/122] Removed option to reply to stdin requests by editing vim-ipython buffer. Use :IPythonInput instead --- ftplugin/python/vim_ipython.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 33283b6..b37113e 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -608,8 +608,7 @@ def update_subchannel_msgs(debug=False, force=False): 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 :py EnteredInsertMode()') - echo('Awaiting input. call :IPythonInput or edit last vim-ipython line') + echo('Awaiting input. call :IPythonInput to reply') if s.find('\n') == -1: # somewhat ugly unicode workaround from @@ -1034,10 +1033,3 @@ def get_session_history(session=None, pattern=None): return [] except KeyError: return [] - -def EnteredInsertMode(): - if current_stdin_prompt and vim.eval('line(".")')==vim.eval('line("$")'): - if InputPrompt(): - vim.command('autocmd InsertLeave :normal! G$') - vim.command('autocmd InsertLeave :autocmd! InsertLeave ') - vim.command('call feedkeys("\\", "n")') From 89b84e11577986b97d77f515489fc932ea303a3e Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Fri, 29 Dec 2017 19:37:33 -0600 Subject: [PATCH 119/122] Handle relative filenames for the connection file --- ftplugin/python/vim_ipython.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index b37113e..0686478 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -167,6 +167,8 @@ def km_from_string(s=''): while not connected and (time.time() - starttime) < 5.0: if not attempt and os.path.isfile(s): fullpath = s + elif not attempt and os.path.isfile(find_connection_file(s)): + fullpath = find_connection_file(s) else: try: s = fullpath = find_connection_file('kernel*') From a8c25b87aa9df26dc900eda7331117fe5fb8ac94 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Sun, 21 Jan 2018 10:01:36 -0700 Subject: [PATCH 120/122] Fix exception on non-existent relative path --- ftplugin/python/vim_ipython.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 0686478..65fe142 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -159,6 +159,12 @@ def km_from_string(s=''): global km, kc, send, history, complete, object_info + def try_find(s): + try: + return os.path.isfile(find_connection_file(s)) + except Exception: + return False + # Test if connection is still alive connected = False starttime = time.time() @@ -167,7 +173,7 @@ def km_from_string(s=''): while not connected and (time.time() - starttime) < 5.0: if not attempt and os.path.isfile(s): fullpath = s - elif not attempt and os.path.isfile(find_connection_file(s)): + elif not attempt and try_find(s): fullpath = find_connection_file(s) else: try: From ff3afa46aaa5dafc1e2797d84a3ec7b1b61d52f6 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Thu, 27 Mar 2025 22:41:47 -0700 Subject: [PATCH 121/122] Update completion_metadata usage --- ftplugin/python/vim_ipython.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 65fe142..9684f50 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -458,7 +458,7 @@ def ipy_complete(base, current_line, pos): try: m = get_child_msg(msg_id, timeout=vim_vars.get('ipython_completion_timeout', 2)) try: - return get_completion_metadata() + return get_completion_metadata(base, m['content']) except KeyError: # completion_metadata function not available matches = m['content']['matches'] metadata = [{} for _ in matches] @@ -467,11 +467,18 @@ def ipy_complete(base, current_line, pos): echo("no reply from IPython kernel") raise IOError -def get_completion_metadata(): +def get_completion_metadata(base, content): """Generate and fetch completion metadata.""" - request = ''' + request = f''' try: - _completions = completion_metadata(get_ipython()) + _completion_args = ( + get_ipython(), + {content['matches']!r}, + {base!r}, + {content['cursor_start']!r}, + {content['cursor_end']!r}, + ) + _completions = completion_metadata(*_completion_args) except Exception: pass ''' From 810a1c8b87080cfbb5f11ec99ac6c49828d179d2 Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Tue, 1 Apr 2025 20:58:35 -0700 Subject: [PATCH 122/122] Clean up invalid escape codes and fix strip_color_escapes --- ftplugin/python/vim_ipython.py | 40 ++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/ftplugin/python/vim_ipython.py b/ftplugin/python/vim_ipython.py index 9684f50..0925760 100644 --- a/ftplugin/python/vim_ipython.py +++ b/ftplugin/python/vim_ipython.py @@ -22,6 +22,7 @@ def __getattribute__(self, key): import ast import os +import re import sys import time PY3 = sys.version_info[0] == 3 @@ -69,7 +70,7 @@ def vim_variable(name, default=None): return vim.eval(name) if exists else default def vim_regex_escape(x): - for old, new in (("[", "\\["), ("]", "\\]"), (":", "\\:"), (".", "\."), ("*", "\\*")): + for old, new in (("[", "\\["), ("]", "\\]"), (":", "\\:"), (".", "\\."), ("*", "\\*")): x = x.replace(old, new) return x @@ -332,11 +333,28 @@ def get_doc(word, level=0): # get around unicode problems when interfacing with vim return [d.encode(vim_encoding) for d in doc] -import re -# from http://serverfault.com/questions/71285/in-centos-4-4-how-can-i-strip-escape-sequences-from-a-text-file -strip = re.compile('\x1B\[([0-9]{1,2}(;[0-9]{1,2})*)?[mK]') +def strip_ansi_colour(text): + """Strip ANSI colour sequences from a string. + + Args: + text (str): Text string to be stripped. + + Returns: + iter[str]: A generator for each returned character. Note, + this will include newline characters. + + """ + import io + buff = io.StringIO(text) + while (b := buff.read(1)): + if b == '\x1b': + while (b := buff.read(1)) != 'm': + continue + else: + yield b + def strip_color_escapes(s): - return strip.sub('',s) + return ''.join(strip_ansi_colour(s)) def get_doc_msg(msg_id): n = 13 # longest field name (empirically) @@ -438,11 +456,11 @@ def get_doc_buffer(level=0, word=None): vim.command('setlocal syntax=python') def ipy_complete(base, current_line, pos): - if re.match('^\s*(import|from)\s+', current_line): + if re.match(r'^\s*(import|from)\s+', current_line): pos -= len(current_line) - len(current_line.lstrip()) current_line = current_line.lstrip() else: - match = re.match('^\s*from\s+\w+(\.\w+)*\s+import\s+(\w+,\s+)*', current_line) + match = re.match(r'^\s*from\s+\w+(\.\w+)*\s+import\s+(\w+,\s+)*', current_line) if match: module = match.string.strip().split()[1] current_line = 'from {module} import {base}'.format( @@ -533,8 +551,8 @@ def update_subchannel_msgs(debug=False, force=False): vim.command( "try" "|silent! wincmd P" - "|catch /^Vim\%((\a\+)\)\=:E441/" - "|silent pedit +set\ ma vim-ipython" + "|catch /^Vim\\%((\\a\\+)\\)\\=:E441/" + "|silent pedit +set\\ ma vim-ipython" "|silent! wincmd P" "|endtry") # if the current window is called 'vim-ipython' @@ -544,7 +562,7 @@ def update_subchannel_msgs(debug=False, force=False): else: # close preview window, it was something other than 'vim-ipython' vim.command("pcl") - vim.command("silent pedit +set\ ma vim-ipython") + vim.command("silent pedit +set\\ ma vim-ipython") vim.command("wincmd P") #switch to preview window # subchannel window quick quit key 'q' vim.command('nnoremap q :q') @@ -825,7 +843,7 @@ def InputPrompt(force=False, hide_input=False): vim.command( "try" "|silent! wincmd P" - "|catch /^Vim\%((\a\+)\)\=:E441/" + "|catch /^Vim\\%((\\a\\+)\\)\\=:E441/" "|endtry") if vim.eval('@%')=='vim-ipython':