##// END OF EJS Templates
Fix completion in indented lines dropping prefix when jedi is disabled (#14474)...
M Bussonnier -
r28832:5c8bc514 merge
parent child Browse files
Show More
@@ -898,7 +898,7 b' def rectify_completions(text: str, completions: _IC, *, _debug: bool = False) ->'
898 new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
898 new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
899 if c._origin == 'jedi':
899 if c._origin == 'jedi':
900 seen_jedi.add(new_text)
900 seen_jedi.add(new_text)
901 elif c._origin == 'IPCompleter.python_matches':
901 elif c._origin == "IPCompleter.python_matcher":
902 seen_python_matches.add(new_text)
902 seen_python_matches.add(new_text)
903 yield Completion(new_start, new_end, new_text, type=c.type, _origin=c._origin, signature=c.signature)
903 yield Completion(new_start, new_end, new_text, type=c.type, _origin=c._origin, signature=c.signature)
904 diff = seen_python_matches.difference(seen_jedi)
904 diff = seen_python_matches.difference(seen_jedi)
@@ -1139,15 +1139,18 b' class Completer(Configurable):'
1139 with a __getattr__ hook is evaluated.
1139 with a __getattr__ hook is evaluated.
1140
1140
1141 """
1141 """
1142 return self._attr_matches(text)[0]
1143
1144 def _attr_matches(self, text, include_prefix=True) -> Tuple[Sequence[str], str]:
1142 m2 = re.match(r"(.+)\.(\w*)$", self.line_buffer)
1145 m2 = re.match(r"(.+)\.(\w*)$", self.line_buffer)
1143 if not m2:
1146 if not m2:
1144 return []
1147 return [], ""
1145 expr, attr = m2.group(1, 2)
1148 expr, attr = m2.group(1, 2)
1146
1149
1147 obj = self._evaluate_expr(expr)
1150 obj = self._evaluate_expr(expr)
1148
1151
1149 if obj is not_found:
1152 if obj is not_found:
1150 return []
1153 return [], ""
1151
1154
1152 if self.limit_to__all__ and hasattr(obj, '__all__'):
1155 if self.limit_to__all__ and hasattr(obj, '__all__'):
1153 words = get__all__entries(obj)
1156 words = get__all__entries(obj)
@@ -1170,6 +1173,7 b' class Completer(Configurable):'
1170 # reconciliator would know that we intend to append to rather than
1173 # reconciliator would know that we intend to append to rather than
1171 # replace the input text; this requires refactoring to return range
1174 # replace the input text; this requires refactoring to return range
1172 # which ought to be replaced (as does jedi).
1175 # which ought to be replaced (as does jedi).
1176 if include_prefix:
1173 tokens = _parse_tokens(expr)
1177 tokens = _parse_tokens(expr)
1174 rev_tokens = reversed(tokens)
1178 rev_tokens = reversed(tokens)
1175 skip_over = {tokenize.ENDMARKER, tokenize.NEWLINE}
1179 skip_over = {tokenize.ENDMARKER, tokenize.NEWLINE}
@@ -1182,7 +1186,9 b' class Completer(Configurable):'
1182 if token.type == tokenize.NAME and name_turn:
1186 if token.type == tokenize.NAME and name_turn:
1183 parts.append(token.string)
1187 parts.append(token.string)
1184 name_turn = False
1188 name_turn = False
1185 elif token.type == tokenize.OP and token.string == "." and not name_turn:
1189 elif (
1190 token.type == tokenize.OP and token.string == "." and not name_turn
1191 ):
1186 parts.append(token.string)
1192 parts.append(token.string)
1187 name_turn = True
1193 name_turn = True
1188 else:
1194 else:
@@ -1190,8 +1196,13 b' class Completer(Configurable):'
1190 break
1196 break
1191
1197
1192 prefix_after_space = "".join(reversed(parts))
1198 prefix_after_space = "".join(reversed(parts))
1199 else:
1200 prefix_after_space = ""
1193
1201
1194 return ["%s.%s" % (prefix_after_space, w) for w in words if w[:n] == attr]
1202 return (
1203 ["%s.%s" % (prefix_after_space, w) for w in words if w[:n] == attr],
1204 "." + attr,
1205 )
1195
1206
1196 def _evaluate_expr(self, expr):
1207 def _evaluate_expr(self, expr):
1197 obj = not_found
1208 obj = not_found
@@ -1973,9 +1984,8 b' class IPCompleter(Completer):'
1973 *self.magic_arg_matchers,
1984 *self.magic_arg_matchers,
1974 self.custom_completer_matcher,
1985 self.custom_completer_matcher,
1975 self.dict_key_matcher,
1986 self.dict_key_matcher,
1976 # TODO: convert python_matches to v2 API
1977 self.magic_matcher,
1987 self.magic_matcher,
1978 self.python_matches,
1988 self.python_matcher,
1979 self.file_matcher,
1989 self.file_matcher,
1980 self.python_func_kw_matcher,
1990 self.python_func_kw_matcher,
1981 ]
1991 ]
@@ -2316,9 +2326,42 b' class IPCompleter(Completer):'
2316 else:
2326 else:
2317 return iter([])
2327 return iter([])
2318
2328
2329 @context_matcher()
2330 def python_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
2331 """Match attributes or global python names"""
2332 text = context.line_with_cursor
2333 if "." in text:
2334 try:
2335 matches, fragment = self._attr_matches(text, include_prefix=False)
2336 if text.endswith(".") and self.omit__names:
2337 if self.omit__names == 1:
2338 # true if txt is _not_ a __ name, false otherwise:
2339 no__name = lambda txt: re.match(r".*\.__.*?__", txt) is None
2340 else:
2341 # true if txt is _not_ a _ name, false otherwise:
2342 no__name = (
2343 lambda txt: re.match(r"\._.*?", txt[txt.rindex(".") :])
2344 is None
2345 )
2346 matches = filter(no__name, matches)
2347 return _convert_matcher_v1_result_to_v2(
2348 matches, type="attribute", fragment=fragment
2349 )
2350 except NameError:
2351 # catches <undefined attributes>.<tab>
2352 matches = []
2353 return _convert_matcher_v1_result_to_v2(matches, type="attribute")
2354 else:
2355 matches = self.global_matches(context.token)
2356 # TODO: maybe distinguish between functions, modules and just "variables"
2357 return _convert_matcher_v1_result_to_v2(matches, type="variable")
2358
2319 @completion_matcher(api_version=1)
2359 @completion_matcher(api_version=1)
2320 def python_matches(self, text: str) -> Iterable[str]:
2360 def python_matches(self, text: str) -> Iterable[str]:
2321 """Match attributes or global python names"""
2361 """Match attributes or global python names.
2362
2363 .. deprecated:: 8.27
2364 You can use :meth:`python_matcher` instead."""
2322 if "." in text:
2365 if "." in text:
2323 try:
2366 try:
2324 matches = self.attr_matches(text)
2367 matches = self.attr_matches(text)
@@ -462,7 +462,10 b' class TestCompleter(unittest.TestCase):'
462 matches = c.all_completions("TestClass.")
462 matches = c.all_completions("TestClass.")
463 assert len(matches) > 2, (jedi_status, matches)
463 assert len(matches) > 2, (jedi_status, matches)
464 matches = c.all_completions("TestClass.a")
464 matches = c.all_completions("TestClass.a")
465 assert matches == ['TestClass.a', 'TestClass.a1'], jedi_status
465 if jedi_status:
466 assert matches == ["TestClass.a", "TestClass.a1"], jedi_status
467 else:
468 assert matches == [".a", ".a1"], jedi_status
466
469
467 @pytest.mark.xfail(
470 @pytest.mark.xfail(
468 sys.version_info.releaselevel in ("alpha",),
471 sys.version_info.releaselevel in ("alpha",),
@@ -594,7 +597,7 b' class TestCompleter(unittest.TestCase):'
594 ip.Completer.use_jedi = True
597 ip.Completer.use_jedi = True
595 with provisionalcompleter():
598 with provisionalcompleter():
596 completions = ip.Completer.completions(line, cursor_pos)
599 completions = ip.Completer.completions(line, cursor_pos)
597 self.assertIn(completion, completions)
600 self.assertIn(completion, list(completions))
598
601
599 with provisionalcompleter():
602 with provisionalcompleter():
600 _(
603 _(
@@ -622,7 +625,7 b' class TestCompleter(unittest.TestCase):'
622 _(
625 _(
623 "assert str.star",
626 "assert str.star",
624 14,
627 14,
625 "str.startswith",
628 ".startswith",
626 "Should have completed on `assert str.star`: %s",
629 "Should have completed on `assert str.star`: %s",
627 Completion(11, 14, "startswith"),
630 Completion(11, 14, "startswith"),
628 )
631 )
@@ -633,6 +636,13 b' class TestCompleter(unittest.TestCase):'
633 "Should have completed on `d['a b'].str`: %s",
636 "Should have completed on `d['a b'].str`: %s",
634 Completion(9, 12, "strip"),
637 Completion(9, 12, "strip"),
635 )
638 )
639 _(
640 "a.app",
641 4,
642 ".append",
643 "Should have completed on `a.app`: %s",
644 Completion(2, 4, "append"),
645 )
636
646
637 def test_omit__names(self):
647 def test_omit__names(self):
638 # also happens to test IPCompleter as a configurable
648 # also happens to test IPCompleter as a configurable
@@ -647,8 +657,8 b' class TestCompleter(unittest.TestCase):'
647 with provisionalcompleter():
657 with provisionalcompleter():
648 c.use_jedi = False
658 c.use_jedi = False
649 s, matches = c.complete("ip.")
659 s, matches = c.complete("ip.")
650 self.assertIn("ip.__str__", matches)
660 self.assertIn(".__str__", matches)
651 self.assertIn("ip._hidden_attr", matches)
661 self.assertIn("._hidden_attr", matches)
652
662
653 # c.use_jedi = True
663 # c.use_jedi = True
654 # completions = set(c.completions('ip.', 3))
664 # completions = set(c.completions('ip.', 3))
@@ -661,7 +671,7 b' class TestCompleter(unittest.TestCase):'
661 with provisionalcompleter():
671 with provisionalcompleter():
662 c.use_jedi = False
672 c.use_jedi = False
663 s, matches = c.complete("ip.")
673 s, matches = c.complete("ip.")
664 self.assertNotIn("ip.__str__", matches)
674 self.assertNotIn(".__str__", matches)
665 # self.assertIn('ip._hidden_attr', matches)
675 # self.assertIn('ip._hidden_attr', matches)
666
676
667 # c.use_jedi = True
677 # c.use_jedi = True
@@ -675,8 +685,8 b' class TestCompleter(unittest.TestCase):'
675 with provisionalcompleter():
685 with provisionalcompleter():
676 c.use_jedi = False
686 c.use_jedi = False
677 s, matches = c.complete("ip.")
687 s, matches = c.complete("ip.")
678 self.assertNotIn("ip.__str__", matches)
688 self.assertNotIn(".__str__", matches)
679 self.assertNotIn("ip._hidden_attr", matches)
689 self.assertNotIn("._hidden_attr", matches)
680
690
681 # c.use_jedi = True
691 # c.use_jedi = True
682 # completions = set(c.completions('ip.', 3))
692 # completions = set(c.completions('ip.', 3))
@@ -686,7 +696,7 b' class TestCompleter(unittest.TestCase):'
686 with provisionalcompleter():
696 with provisionalcompleter():
687 c.use_jedi = False
697 c.use_jedi = False
688 s, matches = c.complete("ip._x.")
698 s, matches = c.complete("ip._x.")
689 self.assertIn("ip._x.keys", matches)
699 self.assertIn(".keys", matches)
690
700
691 # c.use_jedi = True
701 # c.use_jedi = True
692 # completions = set(c.completions('ip._x.', 6))
702 # completions = set(c.completions('ip._x.', 6))
@@ -708,7 +718,7 b' class TestCompleter(unittest.TestCase):'
708 cfg.IPCompleter.limit_to__all__ = False
718 cfg.IPCompleter.limit_to__all__ = False
709 c.update_config(cfg)
719 c.update_config(cfg)
710 s, matches = c.complete("d.")
720 s, matches = c.complete("d.")
711 self.assertIn("d.x", matches)
721 self.assertIn(".x", matches)
712
722
713 def test_get__all__entries_ok(self):
723 def test_get__all__entries_ok(self):
714 class A:
724 class A:
General Comments 0
You need to be logged in to leave comments. Login now