Repository: ipython/ipython

Issue:
Autocompletion of %colors
No autocompletion is implemented for %colors currently.  Arguably a low-priority item.



Discussion:
I'm bumping to 5.0 as I'll refactor the coloring by then.


Resolving patch:
diff --git a/IPython/core/completer.py b/IPython/core/completer.py
--- a/IPython/core/completer.py
+++ b/IPython/core/completer.py
@@ -136,6 +136,7 @@
 from IPython.core.error import TryNext
 from IPython.core.inputsplitter import ESC_MAGIC
 from IPython.core.latex_symbols import latex_symbols, reverse_latex_symbol
+from IPython.core.oinspect import InspectColors
 from IPython.utils import generics
 from IPython.utils.dir2 import dir2, get_real_method
 from IPython.utils.process import arg_split
@@ -1049,11 +1050,14 @@ def __init__(self, shell=None, namespace=None, global_namespace=None,
         self.matchers = [
                          self.python_matches,
                          self.file_matches,
-                         self.magic_config_matches,
                          self.magic_matches,
                          self.python_func_kw_matches,
                          self.dict_key_matches,
                          ]
+        self.magic_arg_matchers = [
+            self.magic_config_matches,
+            self.magic_color_matches,
+        ]
 
         # This is set externally by InteractiveShell
         self.custom_completers = None
@@ -1181,13 +1185,11 @@ def magic_matches(self, text):
 
         return comp
 
-    def magic_config_matches(self, text):
+    def magic_config_matches(self, text:str) -> List[str]:
         """ Match class names and attributes for %config magic """
-        # use line buffer instead of text (which is a word)
-        texts = self.line_buffer.strip().split()
+        texts = text.strip().split()
 
-        if len(texts) > 0 and \
-            ('config'.startswith(texts[0]) or '%config'.startswith(texts[0])):
+        if len(texts) > 0 and (texts[0] == 'config' or texts[0] == '%config'):
             # get all configuration classes
             classes = sorted(set([ c for c in self.shell.configurables
                                    if c.__class__.class_traits(config=True)
@@ -1218,6 +1220,16 @@ def magic_config_matches(self, text):
                          if attr.startswith(texts[1]) ]
         return []
 
+    def magic_color_matches(self, text:str) -> List[str] :
+        """ Match color schemes for %colors magic"""
+        texts = text.strip().split()
+
+        if len(texts) > 0 and (texts[0] == 'colors' or texts[0] == '%colors'):
+            prefix = texts[1] if len(texts) > 1 else ''
+            return [ color for color in InspectColors.keys()
+                     if color.startswith(prefix) ]
+        return []
+
     def _jedi_matches(self, cursor_column:int, cursor_line:int, text:str):
         """
 
@@ -1878,6 +1890,14 @@ def _complete(self, *, cursor_line, cursor_pos, line_buffer=None, text=None,
         self.line_buffer = line_buffer
         self.text_until_cursor = self.line_buffer[:cursor_pos]
 
+        # Do magic arg matches
+        for matcher in self.magic_arg_matchers:
+            matches = [(m, matcher.__qualname__) for m in matcher(line_buffer)]
+            if matches:
+                matches2 = [m[0] for m in matches]
+                origins = [m[1] for m in matches]
+                return text, matches2, origins, ()
+
         # Start with a clean slate of completions
         matches = []
         custom_res = self.dispatch_custom_completer(text)


Test patch:
diff --git a/IPython/core/tests/test_completer.py b/IPython/core/tests/test_completer.py
--- a/IPython/core/tests/test_completer.py
+++ b/IPython/core/tests/test_completer.py
@@ -578,21 +578,22 @@ def test_magic_completion_shadowing():
     nt.assert_equal(matches, ["%matplotlib"])
 
 
-
 def test_magic_config():
     ip = get_ipython()
     c = ip.Completer
 
     s, matches = c.complete(None, 'conf')
     nt.assert_in('%config', matches)
+    s, matches = c.complete(None, 'conf')
+    nt.assert_not_in('AliasManager', matches)
     s, matches = c.complete(None, 'config ')
     nt.assert_in('AliasManager', matches)
     s, matches = c.complete(None, '%config ')
     nt.assert_in('AliasManager', matches)
     s, matches = c.complete(None, 'config Ali')
-    nt.assert_in('AliasManager', matches)
+    nt.assert_list_equal(['AliasManager'], matches)
     s, matches = c.complete(None, '%config Ali')
-    nt.assert_in('AliasManager', matches)
+    nt.assert_list_equal(['AliasManager'], matches)
     s, matches = c.complete(None, 'config AliasManager')
     nt.assert_list_equal(['AliasManager'], matches)
     s, matches = c.complete(None, '%config AliasManager')
@@ -602,9 +603,27 @@ def test_magic_config():
     s, matches = c.complete(None, '%config AliasManager.')
     nt.assert_in('AliasManager.default_aliases', matches)
     s, matches = c.complete(None, 'config AliasManager.de')
-    nt.assert_in('AliasManager.default_aliases', matches)
+    nt.assert_list_equal(['AliasManager.default_aliases'], matches)
     s, matches = c.complete(None, 'config AliasManager.de')
-    nt.assert_in('AliasManager.default_aliases', matches)
+    nt.assert_list_equal(['AliasManager.default_aliases'], matches)
+
+
+def test_magic_color():
+    ip = get_ipython()
+    c = ip.Completer
+
+    s, matches = c.complete(None, 'colo')
+    nt.assert_in('%colors', matches)
+    s, matches = c.complete(None, 'colo')
+    nt.assert_not_in('NoColor', matches)
+    s, matches = c.complete(None, 'colors ')
+    nt.assert_in('NoColor', matches)
+    s, matches = c.complete(None, '%colors ')
+    nt.assert_in('NoColor', matches)
+    s, matches = c.complete(None, 'colors NoCo')
+    nt.assert_list_equal(['NoColor'], matches)
+    s, matches = c.complete(None, '%colors NoCo')
+    nt.assert_list_equal(['NoColor'], matches)
 
 
 def test_match_dict_keys():