##// END OF EJS Templates
Merge pull request #12282 from Carreau/completer-typing
Matthias Bussonnier -
r25717:4999ce9c merge
parent child Browse files
Show More
@@ -0,0 +1,4 b''
1 [mypy]
2 python_version = 3.6
3 ignore_missing_imports = True
4 follow_imports = silent
@@ -40,6 +40,7 b' install:'
40 - pip install trio curio --upgrade --upgrade-strategy eager
40 - pip install trio curio --upgrade --upgrade-strategy eager
41 - pip install pytest 'matplotlib !=3.2.0' mypy
41 - pip install pytest 'matplotlib !=3.2.0' mypy
42 - pip install codecov check-manifest --upgrade
42 - pip install codecov check-manifest --upgrade
43 - pip install mypy
43
44
44 script:
45 script:
45 - check-manifest
46 - check-manifest
@@ -50,7 +51,8 b' script:'
50 fi
51 fi
51 - cd /tmp && iptest --coverage xml && cd -
52 - cd /tmp && iptest --coverage xml && cd -
52 - pytest IPython
53 - pytest IPython
53 - mypy --ignore-missing-imports -m IPython.terminal.ptutils
54 - mypy IPython/terminal/ptutils.py
55 - mypy IPython/core/c*.py
54 # On the latest Python (on Linux) only, make sure that the docs build.
56 # On the latest Python (on Linux) only, make sure that the docs build.
55 - |
57 - |
56 if [[ "$TRAVIS_PYTHON_VERSION" == "3.7" ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
58 if [[ "$TRAVIS_PYTHON_VERSION" == "3.7" ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
@@ -126,7 +126,7 b' import warnings'
126 from contextlib import contextmanager
126 from contextlib import contextmanager
127 from importlib import import_module
127 from importlib import import_module
128 from types import SimpleNamespace
128 from types import SimpleNamespace
129 from typing import Iterable, Iterator, List, Tuple
129 from typing import Iterable, Iterator, List, Tuple, Union, Any, Sequence, Dict, NamedTuple, Pattern, Optional
130
130
131 from IPython.core.error import TryNext
131 from IPython.core.error import TryNext
132 from IPython.core.inputtransformer2 import ESC_MAGIC
132 from IPython.core.inputtransformer2 import ESC_MAGIC
@@ -156,6 +156,14 b' except ImportError:'
156 # Globals
156 # Globals
157 #-----------------------------------------------------------------------------
157 #-----------------------------------------------------------------------------
158
158
159 # ranges where we have most of the valid unicode names. We could be more finer
160 # grained but is it worth it for performace While unicode have character in the
161 # rage 0, 0x110000, we seem to have name for about 10% of those. (131808 as I
162 # write this). With below range we cover them all, with a density of ~67%
163 # biggest next gap we consider only adds up about 1% density and there are 600
164 # gaps that would need hard coding.
165 _UNICODE_RANGES = [(32, 0x2fa1e), (0xe0001, 0xe01f0)]
166
159 # Public API
167 # Public API
160 __all__ = ['Completer','IPCompleter']
168 __all__ = ['Completer','IPCompleter']
161
169
@@ -745,7 +753,7 b' def get__all__entries(obj):'
745 return [w for w in words if isinstance(w, str)]
753 return [w for w in words if isinstance(w, str)]
746
754
747
755
748 def match_dict_keys(keys: List[str], prefix: str, delims: str):
756 def match_dict_keys(keys: List[Union[str, bytes]], prefix: str, delims: str) -> Tuple[str, int, List[str]]:
749 """Used by dict_key_matches, matching the prefix to a list of keys
757 """Used by dict_key_matches, matching the prefix to a list of keys
750
758
751 Parameters
759 Parameters
@@ -766,22 +774,25 b' def match_dict_keys(keys: List[str], prefix: str, delims: str):'
766 ``matches`` a list of replacement/completion
774 ``matches`` a list of replacement/completion
767
775
768 """
776 """
777 keys = [k for k in keys if isinstance(k, (str, bytes))]
769 if not prefix:
778 if not prefix:
770 return None, 0, [repr(k) for k in keys
779 return '', 0, [repr(k) for k in keys
771 if isinstance(k, (str, bytes))]
780 if isinstance(k, (str, bytes))]
772 quote_match = re.search('["\']', prefix)
781 quote_match = re.search('["\']', prefix)
782 assert quote_match is not None # silence mypy
773 quote = quote_match.group()
783 quote = quote_match.group()
774 try:
784 try:
775 prefix_str = eval(prefix + quote, {})
785 prefix_str = eval(prefix + quote, {})
776 except Exception:
786 except Exception:
777 return None, 0, []
787 return '', 0, []
778
788
779 pattern = '[^' + ''.join('\\' + c for c in delims) + ']*$'
789 pattern = '[^' + ''.join('\\' + c for c in delims) + ']*$'
780 token_match = re.search(pattern, prefix, re.UNICODE)
790 token_match = re.search(pattern, prefix, re.UNICODE)
791 assert token_match is not None # silence mypy
781 token_start = token_match.start()
792 token_start = token_match.start()
782 token_prefix = token_match.group()
793 token_prefix = token_match.group()
783
794
784 matched = []
795 matched:List[str] = []
785 for key in keys:
796 for key in keys:
786 try:
797 try:
787 if not key.startswith(prefix_str):
798 if not key.startswith(prefix_str):
@@ -794,14 +805,6 b' def match_dict_keys(keys: List[str], prefix: str, delims: str):'
794 rem = key[len(prefix_str):]
805 rem = key[len(prefix_str):]
795 # force repr wrapped in '
806 # force repr wrapped in '
796 rem_repr = repr(rem + '"') if isinstance(rem, str) else repr(rem + b'"')
807 rem_repr = repr(rem + '"') if isinstance(rem, str) else repr(rem + b'"')
797 if rem_repr.startswith('u') and prefix[0] not in 'uU':
798 # Found key is unicode, but prefix is Py2 string.
799 # Therefore attempt to interpret key as string.
800 try:
801 rem_repr = repr(rem.encode('ascii') + '"')
802 except UnicodeEncodeError:
803 continue
804
805 rem_repr = rem_repr[1 + rem_repr.index("'"):-2]
808 rem_repr = rem_repr[1 + rem_repr.index("'"):-2]
806 if quote == '"':
809 if quote == '"':
807 # The entered prefix is quoted with ",
810 # The entered prefix is quoted with ",
@@ -887,9 +890,8 b' def _safe_isinstance(obj, module, class_name):'
887 return (module in sys.modules and
890 return (module in sys.modules and
888 isinstance(obj, getattr(import_module(module), class_name)))
891 isinstance(obj, getattr(import_module(module), class_name)))
889
892
890
893 def back_unicode_name_matches(text:str) -> Tuple[str, Sequence[str]]:
891 def back_unicode_name_matches(text):
894 """Match Unicode characters back to Unicode name
892 u"""Match unicode characters back to unicode name
893
895
894 This does ``☃`` -> ``\\snowman``
896 This does ``☃`` -> ``\\snowman``
895
897
@@ -898,52 +900,60 b' def back_unicode_name_matches(text):'
898
900
899 This will not either back-complete standard sequences like \\n, \\b ...
901 This will not either back-complete standard sequences like \\n, \\b ...
900
902
901 Used on Python 3 only.
903 Returns
904 =======
905
906 Return a tuple with two elements:
907
908 - The Unicode character that was matched (preceded with a backslash), or
909 empty string,
910 - a sequence (of 1), name for the match Unicode character, preceded by
911 backslash, or empty if no match.
912
902 """
913 """
903 if len(text)<2:
914 if len(text)<2:
904 return u'', ()
915 return '', ()
905 maybe_slash = text[-2]
916 maybe_slash = text[-2]
906 if maybe_slash != '\\':
917 if maybe_slash != '\\':
907 return u'', ()
918 return '', ()
908
919
909 char = text[-1]
920 char = text[-1]
910 # no expand on quote for completion in strings.
921 # no expand on quote for completion in strings.
911 # nor backcomplete standard ascii keys
922 # nor backcomplete standard ascii keys
912 if char in string.ascii_letters or char in ['"',"'"]:
923 if char in string.ascii_letters or char in ('"',"'"):
913 return u'', ()
924 return '', ()
914 try :
925 try :
915 unic = unicodedata.name(char)
926 unic = unicodedata.name(char)
916 return '\\'+char,['\\'+unic]
927 return '\\'+char,('\\'+unic,)
917 except KeyError:
928 except KeyError:
918 pass
929 pass
919 return u'', ()
930 return '', ()
920
931
921 def back_latex_name_matches(text:str):
932 def back_latex_name_matches(text:str) -> Tuple[str, Sequence[str]] :
922 """Match latex characters back to unicode name
933 """Match latex characters back to unicode name
923
934
924 This does ``\\ℵ`` -> ``\\aleph``
935 This does ``\\ℵ`` -> ``\\aleph``
925
936
926 Used on Python 3 only.
927 """
937 """
928 if len(text)<2:
938 if len(text)<2:
929 return u'', ()
939 return '', ()
930 maybe_slash = text[-2]
940 maybe_slash = text[-2]
931 if maybe_slash != '\\':
941 if maybe_slash != '\\':
932 return u'', ()
942 return '', ()
933
943
934
944
935 char = text[-1]
945 char = text[-1]
936 # no expand on quote for completion in strings.
946 # no expand on quote for completion in strings.
937 # nor backcomplete standard ascii keys
947 # nor backcomplete standard ascii keys
938 if char in string.ascii_letters or char in ['"',"'"]:
948 if char in string.ascii_letters or char in ('"',"'"):
939 return u'', ()
949 return '', ()
940 try :
950 try :
941 latex = reverse_latex_symbol[char]
951 latex = reverse_latex_symbol[char]
942 # '\\' replace the \ as well
952 # '\\' replace the \ as well
943 return '\\'+char,[latex]
953 return '\\'+char,[latex]
944 except KeyError:
954 except KeyError:
945 pass
955 pass
946 return u'', ()
956 return '', ()
947
957
948
958
949 def _formatparamchildren(parameter) -> str:
959 def _formatparamchildren(parameter) -> str:
@@ -1002,9 +1012,19 b' def _make_signature(completion)-> str:'
1002 return '(%s)'% ', '.join([f for f in (_formatparamchildren(p) for signature in completion.get_signatures()
1012 return '(%s)'% ', '.join([f for f in (_formatparamchildren(p) for signature in completion.get_signatures()
1003 for p in signature.defined_names()) if f])
1013 for p in signature.defined_names()) if f])
1004
1014
1015
1016 class _CompleteResult(NamedTuple):
1017 matched_text : str
1018 matches: Sequence[str]
1019 matches_origin: Sequence[str]
1020 jedi_matches: Any
1021
1022
1005 class IPCompleter(Completer):
1023 class IPCompleter(Completer):
1006 """Extension of the completer class with IPython-specific features"""
1024 """Extension of the completer class with IPython-specific features"""
1007
1025
1026 __dict_key_regexps: Optional[Dict[bool,Pattern]] = None
1027
1008 @observe('greedy')
1028 @observe('greedy')
1009 def _greedy_changed(self, change):
1029 def _greedy_changed(self, change):
1010 """update the splitter and readline delims when greedy is changed"""
1030 """update the splitter and readline delims when greedy is changed"""
@@ -1143,7 +1163,7 b' class IPCompleter(Completer):'
1143 self._unicode_names = None
1163 self._unicode_names = None
1144
1164
1145 @property
1165 @property
1146 def matchers(self):
1166 def matchers(self) -> List[Any]:
1147 """All active matcher routines for completion"""
1167 """All active matcher routines for completion"""
1148 if self.dict_keys_only:
1168 if self.dict_keys_only:
1149 return [self.dict_key_matches]
1169 return [self.dict_key_matches]
@@ -1165,7 +1185,7 b' class IPCompleter(Completer):'
1165 self.dict_key_matches,
1185 self.dict_key_matches,
1166 ]
1186 ]
1167
1187
1168 def all_completions(self, text) -> List[str]:
1188 def all_completions(self, text:str) -> List[str]:
1169 """
1189 """
1170 Wrapper around the completion methods for the benefit of emacs.
1190 Wrapper around the completion methods for the benefit of emacs.
1171 """
1191 """
@@ -1176,14 +1196,14 b' class IPCompleter(Completer):'
1176
1196
1177 return self.complete(text)[1]
1197 return self.complete(text)[1]
1178
1198
1179 def _clean_glob(self, text):
1199 def _clean_glob(self, text:str):
1180 return self.glob("%s*" % text)
1200 return self.glob("%s*" % text)
1181
1201
1182 def _clean_glob_win32(self,text):
1202 def _clean_glob_win32(self, text:str):
1183 return [f.replace("\\","/")
1203 return [f.replace("\\","/")
1184 for f in self.glob("%s*" % text)]
1204 for f in self.glob("%s*" % text)]
1185
1205
1186 def file_matches(self, text):
1206 def file_matches(self, text:str)->List[str]:
1187 """Match filenames, expanding ~USER type strings.
1207 """Match filenames, expanding ~USER type strings.
1188
1208
1189 Most of the seemingly convoluted logic in this completer is an
1209 Most of the seemingly convoluted logic in this completer is an
@@ -1265,7 +1285,7 b' class IPCompleter(Completer):'
1265 # Mark directories in input list by appending '/' to their names.
1285 # Mark directories in input list by appending '/' to their names.
1266 return [x+'/' if os.path.isdir(x) else x for x in matches]
1286 return [x+'/' if os.path.isdir(x) else x for x in matches]
1267
1287
1268 def magic_matches(self, text):
1288 def magic_matches(self, text:str):
1269 """Match magics"""
1289 """Match magics"""
1270 # Get all shell magics now rather than statically, so magics loaded at
1290 # Get all shell magics now rather than statically, so magics loaded at
1271 # runtime show up too.
1291 # runtime show up too.
@@ -1356,7 +1376,7 b' class IPCompleter(Completer):'
1356 if color.startswith(prefix) ]
1376 if color.startswith(prefix) ]
1357 return []
1377 return []
1358
1378
1359 def _jedi_matches(self, cursor_column:int, cursor_line:int, text:str):
1379 def _jedi_matches(self, cursor_column:int, cursor_line:int, text:str) -> Iterable[Any]:
1360 """
1380 """
1361
1381
1362 Return a list of :any:`jedi.api.Completions` object from a ``text`` and
1382 Return a list of :any:`jedi.api.Completions` object from a ``text`` and
@@ -1430,7 +1450,7 b' class IPCompleter(Completer):'
1430 else:
1450 else:
1431 return []
1451 return []
1432
1452
1433 def python_matches(self, text):
1453 def python_matches(self, text:str)->List[str]:
1434 """Match attributes or global python names"""
1454 """Match attributes or global python names"""
1435 if "." in text:
1455 if "." in text:
1436 try:
1456 try:
@@ -1512,7 +1532,7 b' class IPCompleter(Completer):'
1512
1532
1513 return list(set(ret))
1533 return list(set(ret))
1514
1534
1515 def python_func_kw_matches(self,text):
1535 def python_func_kw_matches(self, text):
1516 """Match named parameters (kwargs) of the last open function"""
1536 """Match named parameters (kwargs) of the last open function"""
1517
1537
1518 if "." in text: # a parameter cannot be dotted
1538 if "." in text: # a parameter cannot be dotted
@@ -1582,36 +1602,39 b' class IPCompleter(Completer):'
1582 # Remove used named arguments from the list, no need to show twice
1602 # Remove used named arguments from the list, no need to show twice
1583 for namedArg in set(namedArgs) - usedNamedArgs:
1603 for namedArg in set(namedArgs) - usedNamedArgs:
1584 if namedArg.startswith(text):
1604 if namedArg.startswith(text):
1585 argMatches.append(u"%s=" %namedArg)
1605 argMatches.append("%s=" %namedArg)
1586 except:
1606 except:
1587 pass
1607 pass
1588
1608
1589 return argMatches
1609 return argMatches
1590
1610
1591 def dict_key_matches(self, text):
1611 @staticmethod
1612 def _get_keys(obj: Any) -> List[Any]:
1613 # Objects can define their own completions by defining an
1614 # _ipy_key_completions_() method.
1615 method = get_real_method(obj, '_ipython_key_completions_')
1616 if method is not None:
1617 return method()
1618
1619 # Special case some common in-memory dict-like types
1620 if isinstance(obj, dict) or\
1621 _safe_isinstance(obj, 'pandas', 'DataFrame'):
1622 try:
1623 return list(obj.keys())
1624 except Exception:
1625 return []
1626 elif _safe_isinstance(obj, 'numpy', 'ndarray') or\
1627 _safe_isinstance(obj, 'numpy', 'void'):
1628 return obj.dtype.names or []
1629 return []
1630
1631 def dict_key_matches(self, text:str) -> List[str]:
1592 "Match string keys in a dictionary, after e.g. 'foo[' "
1632 "Match string keys in a dictionary, after e.g. 'foo[' "
1593 def get_keys(obj):
1594 # Objects can define their own completions by defining an
1595 # _ipy_key_completions_() method.
1596 method = get_real_method(obj, '_ipython_key_completions_')
1597 if method is not None:
1598 return method()
1599
1600 # Special case some common in-memory dict-like types
1601 if isinstance(obj, dict) or\
1602 _safe_isinstance(obj, 'pandas', 'DataFrame'):
1603 try:
1604 return list(obj.keys())
1605 except Exception:
1606 return []
1607 elif _safe_isinstance(obj, 'numpy', 'ndarray') or\
1608 _safe_isinstance(obj, 'numpy', 'void'):
1609 return obj.dtype.names or []
1610 return []
1611
1633
1612 try:
1634
1635 if self.__dict_key_regexps is not None:
1613 regexps = self.__dict_key_regexps
1636 regexps = self.__dict_key_regexps
1614 except AttributeError:
1637 else:
1615 dict_key_re_fmt = r'''(?x)
1638 dict_key_re_fmt = r'''(?x)
1616 ( # match dict-referring expression wrt greedy setting
1639 ( # match dict-referring expression wrt greedy setting
1617 %s
1640 %s
@@ -1651,7 +1674,7 b' class IPCompleter(Completer):'
1651 except Exception:
1674 except Exception:
1652 return []
1675 return []
1653
1676
1654 keys = get_keys(obj)
1677 keys = self._get_keys(obj)
1655 if not keys:
1678 if not keys:
1656 return keys
1679 return keys
1657 closing_quote, token_offset, matches = match_dict_keys(keys, prefix, self.splitter.delims)
1680 closing_quote, token_offset, matches = match_dict_keys(keys, prefix, self.splitter.delims)
@@ -1696,16 +1719,15 b' class IPCompleter(Completer):'
1696
1719
1697 return [leading + k + suf for k in matches]
1720 return [leading + k + suf for k in matches]
1698
1721
1699 def unicode_name_matches(self, text):
1722 @staticmethod
1700 u"""Match Latex-like syntax for unicode characters base
1723 def unicode_name_matches(text:str) -> Tuple[str, List[str]] :
1724 """Match Latex-like syntax for unicode characters base
1701 on the name of the character.
1725 on the name of the character.
1702
1726
1703 This does ``\\GREEK SMALL LETTER ETA`` -> ``η``
1727 This does ``\\GREEK SMALL LETTER ETA`` -> ``η``
1704
1728
1705 Works only on valid python 3 identifier, or on combining characters that
1729 Works only on valid python 3 identifier, or on combining characters that
1706 will combine to form a valid identifier.
1730 will combine to form a valid identifier.
1707
1708 Used on Python 3 only.
1709 """
1731 """
1710 slashpos = text.rfind('\\')
1732 slashpos = text.rfind('\\')
1711 if slashpos > -1:
1733 if slashpos > -1:
@@ -1717,11 +1739,11 b' class IPCompleter(Completer):'
1717 return '\\'+s,[unic]
1739 return '\\'+s,[unic]
1718 except KeyError:
1740 except KeyError:
1719 pass
1741 pass
1720 return u'', []
1742 return '', []
1721
1743
1722
1744
1723 def latex_matches(self, text):
1745 def latex_matches(self, text:str) -> Tuple[str, Sequence[str]]:
1724 u"""Match Latex syntax for unicode characters.
1746 """Match Latex syntax for unicode characters.
1725
1747
1726 This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``α``
1748 This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``α``
1727 """
1749 """
@@ -1738,7 +1760,7 b' class IPCompleter(Completer):'
1738 matches = [k for k in latex_symbols if k.startswith(s)]
1760 matches = [k for k in latex_symbols if k.startswith(s)]
1739 if matches:
1761 if matches:
1740 return s, matches
1762 return s, matches
1741 return u'', []
1763 return '', ()
1742
1764
1743 def dispatch_custom_completer(self, text):
1765 def dispatch_custom_completer(self, text):
1744 if not self.custom_completers:
1766 if not self.custom_completers:
@@ -1839,6 +1861,7 b' class IPCompleter(Completer):'
1839 category=ProvisionalCompleterWarning, stacklevel=2)
1861 category=ProvisionalCompleterWarning, stacklevel=2)
1840
1862
1841 seen = set()
1863 seen = set()
1864 profiler:Optional[cProfile.Profile]
1842 try:
1865 try:
1843 if self.profile_completions:
1866 if self.profile_completions:
1844 import cProfile
1867 import cProfile
@@ -1864,7 +1887,7 b' class IPCompleter(Completer):'
1864 print("Writing profiler output to", output_path)
1887 print("Writing profiler output to", output_path)
1865 profiler.dump_stats(output_path)
1888 profiler.dump_stats(output_path)
1866
1889
1867 def _completions(self, full_text: str, offset: int, *, _timeout)->Iterator[Completion]:
1890 def _completions(self, full_text: str, offset: int, *, _timeout) -> Iterator[Completion]:
1868 """
1891 """
1869 Core completion module.Same signature as :any:`completions`, with the
1892 Core completion module.Same signature as :any:`completions`, with the
1870 extra `timeout` parameter (in seconds).
1893 extra `timeout` parameter (in seconds).
@@ -1949,7 +1972,7 b' class IPCompleter(Completer):'
1949 yield Completion(start=start_offset, end=offset, text=m, _origin=t, signature='', type='<unknown>')
1972 yield Completion(start=start_offset, end=offset, text=m, _origin=t, signature='', type='<unknown>')
1950
1973
1951
1974
1952 def complete(self, text=None, line_buffer=None, cursor_pos=None):
1975 def complete(self, text=None, line_buffer=None, cursor_pos=None) -> Tuple[str, Sequence[str]]:
1953 """Find completions for the given text and line context.
1976 """Find completions for the given text and line context.
1954
1977
1955 Note that both the text and the line_buffer are optional, but at least
1978 Note that both the text and the line_buffer are optional, but at least
@@ -1973,9 +1996,9 b' class IPCompleter(Completer):'
1973
1996
1974 Returns
1997 Returns
1975 -------
1998 -------
1999 Tuple of two items:
1976 text : str
2000 text : str
1977 Text that was actually used in the completion.
2001 Text that was actually used in the completion.
1978
1979 matches : list
2002 matches : list
1980 A list of completion matches.
2003 A list of completion matches.
1981
2004
@@ -1995,7 +2018,7 b' class IPCompleter(Completer):'
1995 return self._complete(line_buffer=line_buffer, cursor_pos=cursor_pos, text=text, cursor_line=0)[:2]
2018 return self._complete(line_buffer=line_buffer, cursor_pos=cursor_pos, text=text, cursor_line=0)[:2]
1996
2019
1997 def _complete(self, *, cursor_line, cursor_pos, line_buffer=None, text=None,
2020 def _complete(self, *, cursor_line, cursor_pos, line_buffer=None, text=None,
1998 full_text=None) -> Tuple[str, List[str], List[str], Iterable[_FakeJediCompletion]]:
2021 full_text=None) -> _CompleteResult:
1999 """
2022 """
2000
2023
2001 Like complete but can also returns raw jedi completions as well as the
2024 Like complete but can also returns raw jedi completions as well as the
@@ -2008,8 +2031,19 b' class IPCompleter(Completer):'
2008 caller) as the offset in the ``text`` or ``line_buffer``, or as the
2031 caller) as the offset in the ``text`` or ``line_buffer``, or as the
2009 ``column`` when passing multiline strings this could/should be renamed
2032 ``column`` when passing multiline strings this could/should be renamed
2010 but would add extra noise.
2033 but would add extra noise.
2034
2035 Return
2036 ======
2037
2038 A tuple of N elements which are (likely):
2039
2040 matched_text: ? the text that the complete matched
2041 matches: list of completions ?
2042 matches_origin: ? list same lenght as matches, and where each completion came from
2043 jedi_matches: list of Jedi matches, have it's own structure.
2011 """
2044 """
2012
2045
2046
2013 # if the cursor position isn't given, the only sane assumption we can
2047 # if the cursor position isn't given, the only sane assumption we can
2014 # make is that it's at the end of the line (the common case)
2048 # make is that it's at the end of the line (the common case)
2015 if cursor_pos is None:
2049 if cursor_pos is None:
@@ -2027,17 +2061,16 b' class IPCompleter(Completer):'
2027 if self.backslash_combining_completions:
2061 if self.backslash_combining_completions:
2028 # allow deactivation of these on windows.
2062 # allow deactivation of these on windows.
2029 base_text = text if not line_buffer else line_buffer[:cursor_pos]
2063 base_text = text if not line_buffer else line_buffer[:cursor_pos]
2030 latex_text, latex_matches = self.latex_matches(base_text)
2064
2031 if latex_matches:
2065 for meth in (self.latex_matches,
2032 return latex_text, latex_matches, ['latex_matches']*len(latex_matches), ()
2066 self.unicode_name_matches,
2033 name_text = ''
2067 back_latex_name_matches,
2034 name_matches = []
2068 back_unicode_name_matches,
2035 # need to add self.fwd_unicode_match() function here when done
2069 self.fwd_unicode_match):
2036 for meth in (self.unicode_name_matches, back_latex_name_matches, back_unicode_name_matches, self.fwd_unicode_match):
2037 name_text, name_matches = meth(base_text)
2070 name_text, name_matches = meth(base_text)
2038 if name_text:
2071 if name_text:
2039 return name_text, name_matches[:MATCHES_LIMIT], \
2072 return _CompleteResult(name_text, name_matches[:MATCHES_LIMIT], \
2040 [meth.__qualname__]*min(len(name_matches), MATCHES_LIMIT), ()
2073 [meth.__qualname__]*min(len(name_matches), MATCHES_LIMIT), ())
2041
2074
2042
2075
2043 # If no line buffer is given, assume the input text is all there was
2076 # If no line buffer is given, assume the input text is all there was
@@ -2052,7 +2085,7 b' class IPCompleter(Completer):'
2052 matches = list(matcher(line_buffer))[:MATCHES_LIMIT]
2085 matches = list(matcher(line_buffer))[:MATCHES_LIMIT]
2053 if matches:
2086 if matches:
2054 origins = [matcher.__qualname__] * len(matches)
2087 origins = [matcher.__qualname__] * len(matches)
2055 return text, matches, origins, ()
2088 return _CompleteResult(text, matches, origins, ())
2056
2089
2057 # Start with a clean slate of completions
2090 # Start with a clean slate of completions
2058 matches = []
2091 matches = []
@@ -2061,13 +2094,13 b' class IPCompleter(Completer):'
2061 # different types of objects. The rlcomplete() method could then
2094 # different types of objects. The rlcomplete() method could then
2062 # simply collapse the dict into a list for readline, but we'd have
2095 # simply collapse the dict into a list for readline, but we'd have
2063 # richer completion semantics in other environments.
2096 # richer completion semantics in other environments.
2064 completions = ()
2097 completions:Iterable[Any] = []
2065 if self.use_jedi:
2098 if self.use_jedi:
2066 if not full_text:
2099 if not full_text:
2067 full_text = line_buffer
2100 full_text = line_buffer
2068 completions = self._jedi_matches(
2101 completions = self._jedi_matches(
2069 cursor_pos, cursor_line, full_text)
2102 cursor_pos, cursor_line, full_text)
2070
2103
2071 if self.merge_completions:
2104 if self.merge_completions:
2072 matches = []
2105 matches = []
2073 for matcher in self.matchers:
2106 for matcher in self.matchers:
@@ -2105,9 +2138,38 b' class IPCompleter(Completer):'
2105
2138
2106 self.matches = _matches
2139 self.matches = _matches
2107
2140
2108 return text, _matches, origins, completions
2141 return _CompleteResult(text, _matches, origins, completions)
2109
2142
2110 def fwd_unicode_match(self, text:str) -> Tuple[str, list]:
2143 def fwd_unicode_match(self, text:str) -> Tuple[str, Sequence[str]]:
2144 """
2145
2146 Forward match a string starting with a backslash with a list of
2147 potential Unicode completions.
2148
2149 Will compute list list of Unicode character names on first call and cache it.
2150
2151 Return
2152 ======
2153
2154 At tuple with:
2155 - matched text (empty if no matches)
2156 - list of potential completions, empty tuple otherwise)
2157 """
2158 # TODO: self.unicode_names is here a list we traverse each time with ~100k elements.
2159 # We could do a faster match using a Trie.
2160
2161 # Using pygtrie the follwing seem to work:
2162
2163 # s = PrefixSet()
2164
2165 # for c in range(0,0x10FFFF + 1):
2166 # try:
2167 # s.add(unicodedata.name(chr(c)))
2168 # except ValueError:
2169 # pass
2170 # [''.join(k) for k in s.iter(prefix)]
2171
2172 # But need to be timed and adds an extra dependency.
2111
2173
2112 slashpos = text.rfind('\\')
2174 slashpos = text.rfind('\\')
2113 # if text starts with slash
2175 # if text starts with slash
@@ -2126,7 +2188,7 b' class IPCompleter(Completer):'
2126
2188
2127 # if text does not start with slash
2189 # if text does not start with slash
2128 else:
2190 else:
2129 return u'', ()
2191 return '', ()
2130
2192
2131 @property
2193 @property
2132 def unicode_names(self) -> List[str]:
2194 def unicode_names(self) -> List[str]:
@@ -2141,6 +2203,16 b' class IPCompleter(Completer):'
2141 names.append(unicodedata.name(chr(c)))
2203 names.append(unicodedata.name(chr(c)))
2142 except ValueError:
2204 except ValueError:
2143 pass
2205 pass
2144 self._unicode_names = names
2206 self._unicode_names = _unicode_name_compute(_UNICODE_RANGES)
2145
2207
2146 return self._unicode_names
2208 return self._unicode_names
2209
2210 def _unicode_name_compute(ranges:List[Tuple[int,int]]) -> List[str]:
2211 names = []
2212 for start,stop in ranges:
2213 for c in range(start, stop) :
2214 try:
2215 names.append(unicodedata.name(chr(c)))
2216 except ValueError:
2217 pass
2218 return names
@@ -33,6 +33,25 b' from nose.tools import assert_in, assert_not_in'
33 # Test functions
33 # Test functions
34 # -----------------------------------------------------------------------------
34 # -----------------------------------------------------------------------------
35
35
36 def test_unicode_range():
37 """
38 Test that the ranges we test for unicode names give the same number of
39 results than testing the full length.
40 """
41 from IPython.core.completer import _unicode_name_compute, _UNICODE_RANGES
42
43 expected_list = _unicode_name_compute([(0, 0x110000)])
44 test = _unicode_name_compute(_UNICODE_RANGES)
45 len_exp = len(expected_list)
46 len_test = len(test)
47
48 # do not inline the len() or on error pytest will try to print the 130 000 +
49 # elements.
50 assert len_exp == len_test
51
52 # fail if new unicode symbols have been added.
53 assert len_exp <= 131808
54
36
55
37 @contextmanager
56 @contextmanager
38 def greedy_completion():
57 def greedy_completion():
@@ -212,9 +231,8 b' class TestCompleter(unittest.TestCase):'
212 keys = random.sample(latex_symbols.keys(), 10)
231 keys = random.sample(latex_symbols.keys(), 10)
213 for k in keys:
232 for k in keys:
214 text, matches = ip.complete(k)
233 text, matches = ip.complete(k)
215 nt.assert_equal(len(matches), 1)
216 nt.assert_equal(text, k)
234 nt.assert_equal(text, k)
217 nt.assert_equal(matches[0], latex_symbols[k])
235 nt.assert_equal(matches, [latex_symbols[k]])
218 # Test a more complex line
236 # Test a more complex line
219 text, matches = ip.complete("print(\\alpha")
237 text, matches = ip.complete("print(\\alpha")
220 nt.assert_equal(text, "\\alpha")
238 nt.assert_equal(text, "\\alpha")
@@ -231,7 +249,7 b' class TestCompleter(unittest.TestCase):'
231 ip = get_ipython()
249 ip = get_ipython()
232 text, matches = ip.Completer.latex_matches("\\really_i_should_match_nothing")
250 text, matches = ip.Completer.latex_matches("\\really_i_should_match_nothing")
233 nt.assert_equal(text, "")
251 nt.assert_equal(text, "")
234 nt.assert_equal(matches, [])
252 nt.assert_equal(matches, ())
235
253
236 def test_back_latex_completion(self):
254 def test_back_latex_completion(self):
237 ip = get_ipython()
255 ip = get_ipython()
@@ -244,14 +262,14 b' class TestCompleter(unittest.TestCase):'
244 ip = get_ipython()
262 ip = get_ipython()
245
263
246 name, matches = ip.complete("\\Ⅴ")
264 name, matches = ip.complete("\\Ⅴ")
247 nt.assert_equal(matches, ["\\ROMAN NUMERAL FIVE"])
265 nt.assert_equal(matches, ("\\ROMAN NUMERAL FIVE",))
248
266
249 def test_forward_unicode_completion(self):
267 def test_forward_unicode_completion(self):
250 ip = get_ipython()
268 ip = get_ipython()
251
269
252 name, matches = ip.complete("\\ROMAN NUMERAL FIVE")
270 name, matches = ip.complete("\\ROMAN NUMERAL FIVE")
253 nt.assert_equal(len(matches), 1)
271 nt.assert_equal(matches, ["Ⅴ"] ) # This is not a V
254 nt.assert_equal(matches[0], "")
272 nt.assert_equal(matches, ["\u2164"] ) # same as above but explicit.
255
273
256 @nt.nottest # now we have a completion for \jmath
274 @nt.nottest # now we have a completion for \jmath
257 @decorators.knownfailureif(
275 @decorators.knownfailureif(
@@ -5,6 +5,7 b' include setupbase.py'
5 include setupegg.py
5 include setupegg.py
6 include MANIFEST.in
6 include MANIFEST.in
7 include pytest.ini
7 include pytest.ini
8 include mypy.ini
8 include .mailmap
9 include .mailmap
9
10
10 recursive-exclude tools *
11 recursive-exclude tools *
General Comments 0
You need to be logged in to leave comments. Login now