From b29fa5a8e76babe5b7c34fd5bfee159c7a24000a 2012-05-26 06:50:20 From: Fernando Perez Date: 2012-05-26 06:50:20 Subject: [PATCH] Add completion support for cell magics and handling of line/cell magics. Tests included. --- diff --git a/IPython/core/completer.py b/IPython/core/completer.py index d451fcd..52fa216 100644 --- a/IPython/core/completer.py +++ b/IPython/core/completer.py @@ -53,15 +53,14 @@ used, and this module (and the readline module) are silently inactive. # proper procedure is to maintain its copyright as belonging to the Python # Software Foundation (in addition to my own, for all new code). # -# Copyright (C) 2008-2011 IPython Development Team -# Copyright (C) 2001-2007 Fernando Perez. +# Copyright (C) 2008 IPython Development Team +# Copyright (C) 2001 Fernando Perez. # Copyright (C) 2001 Python Software Foundation, www.python.org # # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. # #***************************************************************************** -from __future__ import print_function #----------------------------------------------------------------------------- # Imports @@ -606,12 +605,23 @@ class IPCompleter(Completer): """Match magics""" #print 'Completer->magic_matches:',text,'lb',self.text_until_cursor # dbg # Get all shell magics now rather than statically, so magics loaded at - # runtime show up too - # FIXME - cell magics not implemented here yet. - magics = self.shell.magics_manager.lsmagic()['line'] + # runtime show up too. + lsm = self.shell.magics_manager.lsmagic() + line_magics = lsm['line'] + cell_magics = lsm['cell'] pre = self.magic_escape - baretext = text.lstrip(pre) - return [ pre+m for m in magics if m.startswith(baretext)] + pre2 = pre+pre + + # Completion logic: + # - user gives %%: only do cell magics + # - user gives %: do both line and cell magics + # - no prefix: do both + # In other words, line magics are skipped if the user gives %% explicitly + bare_text = text.lstrip(pre) + comp = [ pre2+m for m in cell_magics if m.startswith(bare_text)] + if not text.startswith(pre2): + comp += [ pre+m for m in line_magics if m.startswith(bare_text)] + return comp def alias_matches(self, text): """Match internal system aliases""" diff --git a/IPython/core/tests/test_completer.py b/IPython/core/tests/test_completer.py index 15285cc..c0bddd8 100644 --- a/IPython/core/tests/test_completer.py +++ b/IPython/core/tests/test_completer.py @@ -290,3 +290,54 @@ def test_func_kw_completions(): # Simulate completing with cursor right after b (pos==10): s, matches = c.complete(None,'myfunc(1,b)', 10) nt.assert_in('b=', matches) + + +def test_line_magics(): + ip = get_ipython() + c = ip.Completer + s, matches = c.complete(None, 'lsmag') + nt.assert_in('%lsmagic', matches) + s, matches = c.complete(None, '%lsmag') + nt.assert_in('%lsmagic', matches) + + +def test_cell_magics(): + from IPython.core.magic import register_cell_magic + + @register_cell_magic + def _foo_cellm(line, cell): + pass + + ip = get_ipython() + c = ip.Completer + + s, matches = c.complete(None, '_foo_ce') + nt.assert_in('%%_foo_cellm', matches) + s, matches = c.complete(None, '%%_foo_ce') + nt.assert_in('%%_foo_cellm', matches) + + +def test_line_cell_magics(): + from IPython.core.magic import register_line_cell_magic + + @register_line_cell_magic + def _bar_cellm(line, cell): + pass + + ip = get_ipython() + c = ip.Completer + + # The policy here is trickier, see comments in completion code. The + # returned values depend on whether the user passes %% or not explicitly, + # and this will show a difference if the same name is both a line and cell + # magic. + s, matches = c.complete(None, '_bar_ce') + nt.assert_in('%_bar_cellm', matches) + nt.assert_in('%%_bar_cellm', matches) + s, matches = c.complete(None, '%_bar_ce') + nt.assert_in('%_bar_cellm', matches) + nt.assert_in('%%_bar_cellm', matches) + s, matches = c.complete(None, '%%_bar_ce') + nt.assert_not_in('%_bar_cellm', matches) + nt.assert_in('%%_bar_cellm', matches) +