Show More
@@ -101,12 +101,10 b' option.' | |||||
101 | Be sure to update :any:`jedi` to the latest stable version or to try the |
|
101 | Be sure to update :any:`jedi` to the latest stable version or to try the | |
102 | current development version to get better completions. |
|
102 | current development version to get better completions. | |
103 |
|
103 | |||
104 | .. _Matchers: |
|
|||
105 |
|
||||
106 | Matchers |
|
104 | Matchers | |
107 | ======== |
|
105 | ======== | |
108 |
|
106 | |||
109 |
All completions routines are implemented using unified |
|
107 | All completions routines are implemented using unified *Matchers* API. | |
110 | The matchers API is provisional and subject to change without notice. |
|
108 | The matchers API is provisional and subject to change without notice. | |
111 |
|
109 | |||
112 | The built-in matchers include: |
|
110 | The built-in matchers include: | |
@@ -119,13 +117,26 b' The built-in matchers include:' | |||||
119 | - ``IPCompleter.python_func_kw_matcher`` - function keywords, |
|
117 | - ``IPCompleter.python_func_kw_matcher`` - function keywords, | |
120 | - ``IPCompleter.python_matches`` - globals and attributes (v1 API), |
|
118 | - ``IPCompleter.python_matches`` - globals and attributes (v1 API), | |
121 | - ``IPCompleter.jedi_matcher`` - static analysis with Jedi, |
|
119 | - ``IPCompleter.jedi_matcher`` - static analysis with Jedi, | |
122 |
- ``IPCompleter.custom_completer_matcher`` - pluggable completer with a default implementation in |
|
120 | - ``IPCompleter.custom_completer_matcher`` - pluggable completer with a default implementation in any:`core.InteractiveShell` | |
123 |
|
|
121 | which uses uses IPython hooks system (`complete_command`) with string dispatch (including regular expressions). | |
124 |
|
|
122 | Differently to other matchers, ``custom_completer_matcher`` will not suppress Jedi results to match | |
125 |
|
|
123 | behaviour in earlier IPython versions. | |
|
124 | ||||
|
125 | Custom matchers can be added by appending to ``IPCompleter.custom_matchers`` list. | |||
|
126 | ||||
|
127 | Suppression of competing matchers | |||
|
128 | --------------------------------- | |||
|
129 | ||||
|
130 | By default results from all matchers are combined, in the order determined by | |||
|
131 | their priority. Matchers can request to suppress results from subsequent | |||
|
132 | matchers by setting ``suppress`` to ``True`` in the ``MatcherResult``. | |||
126 |
|
133 | |||
127 | Adding custom matchers is possible by appending to `IPCompleter.custom_matchers` list, |
|
134 | When multiple matchers simultaneously request surpression, the results from of | |
128 | but please be aware that this API is subject to change. |
|
135 | the matcher with higher priority will be returned. | |
|
136 | ||||
|
137 | Sometimes it is desirable to suppress most but not all other matchers; | |||
|
138 | this can be achieved by adding a list of identifiers of matchers which | |||
|
139 | should not be suppressed to ``MatcherResult`` under ``do_not_suppress`` key. | |||
129 | """ |
|
140 | """ | |
130 |
|
141 | |||
131 |
|
142 | |||
@@ -231,7 +242,7 b' else:' | |||||
231 | _UNICODE_RANGES = [(32, 0x3134b), (0xe0001, 0xe01f0)] |
|
242 | _UNICODE_RANGES = [(32, 0x3134b), (0xe0001, 0xe01f0)] | |
232 |
|
243 | |||
233 | # Public API |
|
244 | # Public API | |
234 |
__all__ = [ |
|
245 | __all__ = ["Completer", "IPCompleter"] | |
235 |
|
246 | |||
236 | if sys.platform == 'win32': |
|
247 | if sys.platform == 'win32': | |
237 | PROTECTABLES = ' ' |
|
248 | PROTECTABLES = ' ' | |
@@ -427,7 +438,7 b' _JediCompletionLike = Union[jedi.api.Completion, _FakeJediCompletion]' | |||||
427 |
|
438 | |||
428 | class Completion: |
|
439 | class Completion: | |
429 | """ |
|
440 | """ | |
430 | Completion object used and return by IPython completers. |
|
441 | Completion object used and returned by IPython completers. | |
431 |
|
442 | |||
432 | .. warning:: |
|
443 | .. warning:: | |
433 |
|
444 | |||
@@ -488,12 +499,19 b' class Completion:' | |||||
488 |
|
499 | |||
489 |
|
500 | |||
490 | class SimpleCompletion: |
|
501 | class SimpleCompletion: | |
491 | # TODO: decide whether we should keep the ``SimpleCompletion`` separate from ``Completion`` |
|
502 | """Completion item to be included in the dictionary returned by new-style Matcher (API v2). | |
492 | # there are two advantages of keeping them separate: |
|
503 | ||
493 | # - compatibility with old readline `Completer.complete` interface (less important) |
|
504 | .. warning:: | |
494 | # - ease of use for third parties (just return matched text and don't worry about coordinates) |
|
505 | ||
495 | # the disadvantage is that we need to loop over the completions again to transform them into |
|
506 | Provisional | |
496 | # `Completion` objects (but it was done like that before the refactor into `SimpleCompletion` too). |
|
507 | ||
|
508 | This class is used to describe the currently supported attributes of | |||
|
509 | simple completion items, and any additional implementation details | |||
|
510 | should not be relied on. Additional attributes may be included in | |||
|
511 | future versions, and meaning of text disambiguated from the current | |||
|
512 | dual meaning of "text to insert" and "text to used as a label". | |||
|
513 | """ | |||
|
514 | ||||
497 | __slots__ = ["text", "type"] |
|
515 | __slots__ = ["text", "type"] | |
498 |
|
516 | |||
499 | def __init__(self, text: str, *, type: str = None): |
|
517 | def __init__(self, text: str, *, type: str = None): | |
@@ -504,30 +522,31 b' class SimpleCompletion:' | |||||
504 | return f"<SimpleCompletion text={self.text!r} type={self.type!r}>" |
|
522 | return f"<SimpleCompletion text={self.text!r} type={self.type!r}>" | |
505 |
|
523 | |||
506 |
|
524 | |||
507 |
class |
|
525 | class MatcherResultBase(TypedDict): | |
|
526 | """Definition of dictionary to be returned by new-style Matcher (API v2).""" | |||
508 |
|
527 | |||
509 | #: suffix of the provided ``CompletionContext.token``, if not given defaults to full token. |
|
528 | #: suffix of the provided ``CompletionContext.token``, if not given defaults to full token. | |
510 | matched_fragment: NotRequired[str] |
|
529 | matched_fragment: NotRequired[str] | |
511 |
|
530 | |||
512 |
#: whether to suppress results from other matchers |
|
531 | #: whether to suppress results from all other matchers (True), some | |
513 | suppress_others: NotRequired[bool] |
|
532 | #: matchers (set of identifiers) or none (False); default is False. | |
|
533 | suppress: NotRequired[Union[bool, Set[str]]] | |||
|
534 | ||||
|
535 | #: identifiers of matchers which should NOT be suppressed | |||
|
536 | do_not_suppress: NotRequired[Set[str]] | |||
514 |
|
537 | |||
515 | #: are completions already ordered and should be left as-is? default is False. |
|
538 | #: are completions already ordered and should be left as-is? default is False. | |
516 | ordered: NotRequired[bool] |
|
539 | ordered: NotRequired[bool] | |
517 |
|
540 | |||
518 | # TODO: should we used a relevance score for ordering? |
|
|||
519 | #: value between 0 (likely not relevant) and 100 (likely relevant); default is 50. |
|
|||
520 | # relevance: NotRequired[float] |
|
|||
521 |
|
541 | |||
522 |
|
542 | class SimpleMatcherResult(MatcherResultBase): | ||
523 | class SimpleMatcherResult(_MatcherResultBase): |
|
|||
524 | """Result of new-style completion matcher.""" |
|
543 | """Result of new-style completion matcher.""" | |
525 |
|
544 | |||
526 | #: list of candidate completions |
|
545 | #: list of candidate completions | |
527 | completions: Sequence[SimpleCompletion] |
|
546 | completions: Sequence[SimpleCompletion] | |
528 |
|
547 | |||
529 |
|
548 | |||
530 |
class _JediMatcherResult( |
|
549 | class _JediMatcherResult(MatcherResultBase): | |
531 | """Matching result returned by Jedi (will be processed differently)""" |
|
550 | """Matching result returned by Jedi (will be processed differently)""" | |
532 |
|
551 | |||
533 | #: list of candidate completions |
|
552 | #: list of candidate completions | |
@@ -535,6 +554,8 b' class _JediMatcherResult(_MatcherResultBase):' | |||||
535 |
|
554 | |||
536 |
|
555 | |||
537 | class CompletionContext(NamedTuple): |
|
556 | class CompletionContext(NamedTuple): | |
|
557 | """Completion context provided as an argument to matchers in the Matcher API v2.""" | |||
|
558 | ||||
538 | # rationale: many legacy matchers relied on completer state (`self.text_until_cursor`) |
|
559 | # rationale: many legacy matchers relied on completer state (`self.text_until_cursor`) | |
539 | # which was not explicitly visible as an argument of the matcher, making any refactor |
|
560 | # which was not explicitly visible as an argument of the matcher, making any refactor | |
540 | # prone to errors; by explicitly passing `cursor_position` we can decouple the matchers |
|
561 | # prone to errors; by explicitly passing `cursor_position` we can decouple the matchers | |
@@ -546,14 +567,20 b' class CompletionContext(NamedTuple):' | |||||
546 | #: (by switching the greedy mode). |
|
567 | #: (by switching the greedy mode). | |
547 | token: str |
|
568 | token: str | |
548 |
|
569 | |||
|
570 | #: The full available content of the editor or buffer | |||
549 | full_text: str |
|
571 | full_text: str | |
550 |
|
572 | |||
551 | #: Cursor position in the line (the same for ``full_text`` and `text``). |
|
573 | #: Cursor position in the line (the same for ``full_text`` and ``text``). | |
552 | cursor_position: int |
|
574 | cursor_position: int | |
553 |
|
575 | |||
554 | #: Cursor line in ``full_text``. |
|
576 | #: Cursor line in ``full_text``. | |
555 | cursor_line: int |
|
577 | cursor_line: int | |
556 |
|
578 | |||
|
579 | #: The maximum number of completions that will be used downstream. | |||
|
580 | #: Matchers can use this information to abort early. | |||
|
581 | #: The built-in Jedi matcher is currently excepted from this limit. | |||
|
582 | limit: int | |||
|
583 | ||||
557 | @property |
|
584 | @property | |
558 | @lru_cache(maxsize=None) # TODO change to @cache after dropping Python 3.7 |
|
585 | @lru_cache(maxsize=None) # TODO change to @cache after dropping Python 3.7 | |
559 | def text_until_cursor(self) -> str: |
|
586 | def text_until_cursor(self) -> str: | |
@@ -573,7 +600,7 b' Matcher = Union[MatcherAPIv1, MatcherAPIv2]' | |||||
573 |
|
600 | |||
574 |
|
601 | |||
575 | def completion_matcher( |
|
602 | def completion_matcher( | |
576 | *, priority: float = None, identifier: str = None, api_version=1 |
|
603 | *, priority: float = None, identifier: str = None, api_version: int = 1 | |
577 | ): |
|
604 | ): | |
578 | """Adds attributes describing the matcher. |
|
605 | """Adds attributes describing the matcher. | |
579 |
|
606 | |||
@@ -581,7 +608,7 b' def completion_matcher(' | |||||
581 | ---------- |
|
608 | ---------- | |
582 | priority : Optional[float] |
|
609 | priority : Optional[float] | |
583 | The priority of the matcher, determines the order of execution of matchers. |
|
610 | The priority of the matcher, determines the order of execution of matchers. | |
584 |
Higher priority means that the matcher will be executed first. Defaults to |
|
611 | Higher priority means that the matcher will be executed first. Defaults to 0. | |
585 | identifier : Optional[str] |
|
612 | identifier : Optional[str] | |
586 | identifier of the matcher allowing users to modify the behaviour via traitlets, |
|
613 | identifier of the matcher allowing users to modify the behaviour via traitlets, | |
587 | and also used to for debugging (will be passed as ``origin`` with the completions). |
|
614 | and also used to for debugging (will be passed as ``origin`` with the completions). | |
@@ -593,14 +620,23 b' def completion_matcher(' | |||||
593 | """ |
|
620 | """ | |
594 |
|
621 | |||
595 | def wrapper(func: Matcher): |
|
622 | def wrapper(func: Matcher): | |
596 | func.matcher_priority = priority |
|
623 | func.matcher_priority = priority or 0 | |
597 | func.matcher_identifier = identifier or func.__qualname__ |
|
624 | func.matcher_identifier = identifier or func.__qualname__ | |
598 | func.matcher_api_version = api_version |
|
625 | func.matcher_api_version = api_version | |
|
626 | if TYPE_CHECKING: | |||
|
627 | if api_version == 1: | |||
|
628 | func = cast(func, MatcherAPIv1) | |||
|
629 | elif api_version == 2: | |||
|
630 | func = cast(func, MatcherAPIv2) | |||
599 | return func |
|
631 | return func | |
600 |
|
632 | |||
601 | return wrapper |
|
633 | return wrapper | |
602 |
|
634 | |||
603 |
|
635 | |||
|
636 | def _get_matcher_priority(matcher: Matcher): | |||
|
637 | return getattr(matcher, "matcher_priority", 0) | |||
|
638 | ||||
|
639 | ||||
604 | def _get_matcher_id(matcher: Matcher): |
|
640 | def _get_matcher_id(matcher: Matcher): | |
605 | return getattr(matcher, "matcher_identifier", matcher.__qualname__) |
|
641 | return getattr(matcher, "matcher_identifier", matcher.__qualname__) | |
606 |
|
642 | |||
@@ -1118,6 +1154,10 b' def _safe_isinstance(obj, module, class_name):' | |||||
1118 |
|
1154 | |||
1119 | @context_matcher() |
|
1155 | @context_matcher() | |
1120 | def back_unicode_name_matcher(context): |
|
1156 | def back_unicode_name_matcher(context): | |
|
1157 | """Match Unicode characters back to Unicode name | |||
|
1158 | ||||
|
1159 | Same as ``back_unicode_name_matches``, but adopted to new Matcher API. | |||
|
1160 | """ | |||
1121 | fragment, matches = back_unicode_name_matches(context.token) |
|
1161 | fragment, matches = back_unicode_name_matches(context.token) | |
1122 | return _convert_matcher_v1_result_to_v2( |
|
1162 | return _convert_matcher_v1_result_to_v2( | |
1123 | matches, type="unicode", fragment=fragment, suppress_if_matches=True |
|
1163 | matches, type="unicode", fragment=fragment, suppress_if_matches=True | |
@@ -1166,6 +1206,10 b' def back_unicode_name_matches(text: str) -> Tuple[str, Sequence[str]]:' | |||||
1166 |
|
1206 | |||
1167 | @context_matcher() |
|
1207 | @context_matcher() | |
1168 | def back_latex_name_matcher(context): |
|
1208 | def back_latex_name_matcher(context): | |
|
1209 | """Match latex characters back to unicode name | |||
|
1210 | ||||
|
1211 | Same as ``back_latex_name_matches``, but adopted to new Matcher API. | |||
|
1212 | """ | |||
1169 | fragment, matches = back_latex_name_matches(context.token) |
|
1213 | fragment, matches = back_latex_name_matches(context.token) | |
1170 | return _convert_matcher_v1_result_to_v2( |
|
1214 | return _convert_matcher_v1_result_to_v2( | |
1171 | matches, type="latex", fragment=fragment, suppress_if_matches=True |
|
1215 | matches, type="latex", fragment=fragment, suppress_if_matches=True | |
@@ -1263,9 +1307,7 b' def _convert_matcher_v1_result_to_v2(' | |||||
1263 | """Utility to help with transition""" |
|
1307 | """Utility to help with transition""" | |
1264 | result = { |
|
1308 | result = { | |
1265 | "completions": [SimpleCompletion(text=match, type=type) for match in matches], |
|
1309 | "completions": [SimpleCompletion(text=match, type=type) for match in matches], | |
1266 |
"suppress |
|
1310 | "suppress": (True if matches else False) if suppress_if_matches else False, | |
1267 | if suppress_if_matches |
|
|||
1268 | else False, |
|
|||
1269 | } |
|
1311 | } | |
1270 | if fragment is not None: |
|
1312 | if fragment is not None: | |
1271 | result["matched_fragment"] = fragment |
|
1313 | result["matched_fragment"] = fragment | |
@@ -1297,7 +1339,7 b' class IPCompleter(Completer):' | |||||
1297 | suppress_competing_matchers = UnionTrait( |
|
1339 | suppress_competing_matchers = UnionTrait( | |
1298 | [Bool(), DictTrait(Bool(None, allow_none=True))], |
|
1340 | [Bool(), DictTrait(Bool(None, allow_none=True))], | |
1299 | help=""" |
|
1341 | help=""" | |
1300 |
Whether to suppress completions from other |
|
1342 | Whether to suppress completions from other *Matchers*. | |
1301 |
|
1343 | |||
1302 | When set to ``None`` (default) the matchers will attempt to auto-detect |
|
1344 | When set to ``None`` (default) the matchers will attempt to auto-detect | |
1303 | whether suppression of other matchers is desirable. For example, at |
|
1345 | whether suppression of other matchers is desirable. For example, at | |
@@ -1312,7 +1354,7 b' class IPCompleter(Completer):' | |||||
1312 |
|
1354 | |||
1313 | Set ``IPCompleter.suppress_competing_matchers = True`` to limit |
|
1355 | Set ``IPCompleter.suppress_competing_matchers = True`` to limit | |
1314 | completions to the set of matchers with the highest priority; |
|
1356 | completions to the set of matchers with the highest priority; | |
1315 |
this is equivalent to ``IPCompleter.merge_completions`` and |
|
1357 | this is equivalent to ``IPCompleter.merge_completions`` and | |
1316 | can be beneficial for performance, but will sometimes omit relevant |
|
1358 | can be beneficial for performance, but will sometimes omit relevant | |
1317 | candidates from matchers further down the priority list. |
|
1359 | candidates from matchers further down the priority list. | |
1318 | """, |
|
1360 | """, | |
@@ -1325,7 +1367,7 b' class IPCompleter(Completer):' | |||||
1325 | If False, only the completion results from the first non-empty |
|
1367 | If False, only the completion results from the first non-empty | |
1326 | completer will be returned. |
|
1368 | completer will be returned. | |
1327 |
|
1369 | |||
1328 |
As of version 8. |
|
1370 | As of version 8.6.0, setting the value to ``False`` is an alias for: | |
1329 | ``IPCompleter.suppress_competing_matchers = True.``. |
|
1371 | ``IPCompleter.suppress_competing_matchers = True.``. | |
1330 | """, |
|
1372 | """, | |
1331 | ).tag(config=True) |
|
1373 | ).tag(config=True) | |
@@ -1469,9 +1511,6 b' class IPCompleter(Completer):' | |||||
1469 | if not self.merge_completions: |
|
1511 | if not self.merge_completions: | |
1470 | self.suppress_competing_matchers = True |
|
1512 | self.suppress_competing_matchers = True | |
1471 |
|
1513 | |||
1472 | if self.dict_keys_only: |
|
|||
1473 | self.disable_matchers.append(self.dict_key_matcher.matcher_identifier) |
|
|||
1474 |
|
||||
1475 | @property |
|
1514 | @property | |
1476 | def matchers(self) -> List[Matcher]: |
|
1515 | def matchers(self) -> List[Matcher]: | |
1477 | """All active matcher routines for completion""" |
|
1516 | """All active matcher routines for completion""" | |
@@ -1523,6 +1562,7 b' class IPCompleter(Completer):' | |||||
1523 |
|
1562 | |||
1524 | @context_matcher() |
|
1563 | @context_matcher() | |
1525 | def file_matcher(self, context: CompletionContext) -> SimpleMatcherResult: |
|
1564 | def file_matcher(self, context: CompletionContext) -> SimpleMatcherResult: | |
|
1565 | """Same as ``file_matches``, but adopted to new Matcher API.""" | |||
1526 | matches = self.file_matches(context.token) |
|
1566 | matches = self.file_matches(context.token) | |
1527 | # TODO: add a heuristic for suppressing (e.g. if it has OS-specific delimiter, |
|
1567 | # TODO: add a heuristic for suppressing (e.g. if it has OS-specific delimiter, | |
1528 | # starts with `/home/`, `C:\`, etc) |
|
1568 | # starts with `/home/`, `C:\`, etc) | |
@@ -1540,7 +1580,10 b' class IPCompleter(Completer):' | |||||
1540 | only the parts after what's already been typed (instead of the |
|
1580 | only the parts after what's already been typed (instead of the | |
1541 | full completions, as is normally done). I don't think with the |
|
1581 | full completions, as is normally done). I don't think with the | |
1542 | current (as of Python 2.3) Python readline it's possible to do |
|
1582 | current (as of Python 2.3) Python readline it's possible to do | |
1543 |
better. |
|
1583 | better. | |
|
1584 | ||||
|
1585 | DEPRECATED: Deprecated since 8.6. Use ``file_matcher`` instead. | |||
|
1586 | """ | |||
1544 |
|
1587 | |||
1545 | # chars that require escaping with backslash - i.e. chars |
|
1588 | # chars that require escaping with backslash - i.e. chars | |
1546 | # that readline treats incorrectly as delimiters, but we |
|
1589 | # that readline treats incorrectly as delimiters, but we | |
@@ -1616,11 +1659,14 b' class IPCompleter(Completer):' | |||||
1616 | matches = self.magic_matches(text) |
|
1659 | matches = self.magic_matches(text) | |
1617 | result = _convert_matcher_v1_result_to_v2(matches, type="magic") |
|
1660 | result = _convert_matcher_v1_result_to_v2(matches, type="magic") | |
1618 | is_magic_prefix = len(text) > 0 and text[0] == "%" |
|
1661 | is_magic_prefix = len(text) > 0 and text[0] == "%" | |
1619 |
result["suppress |
|
1662 | result["suppress"] = is_magic_prefix and bool(result["completions"]) | |
1620 | return result |
|
1663 | return result | |
1621 |
|
1664 | |||
1622 | def magic_matches(self, text: str): |
|
1665 | def magic_matches(self, text: str): | |
1623 |
"""Match magics |
|
1666 | """Match magics. | |
|
1667 | ||||
|
1668 | DEPRECATED: Deprecated since 8.6. Use ``magic_matcher`` instead. | |||
|
1669 | """ | |||
1624 | # Get all shell magics now rather than statically, so magics loaded at |
|
1670 | # Get all shell magics now rather than statically, so magics loaded at | |
1625 | # runtime show up too. |
|
1671 | # runtime show up too. | |
1626 | lsm = self.shell.magics_manager.lsmagic() |
|
1672 | lsm = self.shell.magics_manager.lsmagic() | |
@@ -1663,12 +1709,16 b' class IPCompleter(Completer):' | |||||
1663 |
|
1709 | |||
1664 | @context_matcher() |
|
1710 | @context_matcher() | |
1665 | def magic_config_matcher(self, context: CompletionContext) -> SimpleMatcherResult: |
|
1711 | def magic_config_matcher(self, context: CompletionContext) -> SimpleMatcherResult: | |
|
1712 | """Match class names and attributes for %config magic.""" | |||
1666 | # NOTE: uses `line_buffer` equivalent for compatibility |
|
1713 | # NOTE: uses `line_buffer` equivalent for compatibility | |
1667 | matches = self.magic_config_matches(context.line_with_cursor) |
|
1714 | matches = self.magic_config_matches(context.line_with_cursor) | |
1668 | return _convert_matcher_v1_result_to_v2(matches, type="param") |
|
1715 | return _convert_matcher_v1_result_to_v2(matches, type="param") | |
1669 |
|
1716 | |||
1670 | def magic_config_matches(self, text: str) -> List[str]: |
|
1717 | def magic_config_matches(self, text: str) -> List[str]: | |
1671 |
"""Match class names and attributes for %config magic |
|
1718 | """Match class names and attributes for %config magic. | |
|
1719 | ||||
|
1720 | DEPRECATED: Deprecated since 8.6. Use ``magic_config_matcher`` instead. | |||
|
1721 | """ | |||
1672 | texts = text.strip().split() |
|
1722 | texts = text.strip().split() | |
1673 |
|
1723 | |||
1674 | if len(texts) > 0 and (texts[0] == 'config' or texts[0] == '%config'): |
|
1724 | if len(texts) > 0 and (texts[0] == 'config' or texts[0] == '%config'): | |
@@ -1704,12 +1754,16 b' class IPCompleter(Completer):' | |||||
1704 |
|
1754 | |||
1705 | @context_matcher() |
|
1755 | @context_matcher() | |
1706 | def magic_color_matcher(self, context: CompletionContext) -> SimpleMatcherResult: |
|
1756 | def magic_color_matcher(self, context: CompletionContext) -> SimpleMatcherResult: | |
|
1757 | """Match color schemes for %colors magic.""" | |||
1707 | # NOTE: uses `line_buffer` equivalent for compatibility |
|
1758 | # NOTE: uses `line_buffer` equivalent for compatibility | |
1708 | matches = self.magic_color_matches(context.line_with_cursor) |
|
1759 | matches = self.magic_color_matches(context.line_with_cursor) | |
1709 | return _convert_matcher_v1_result_to_v2(matches, type="param") |
|
1760 | return _convert_matcher_v1_result_to_v2(matches, type="param") | |
1710 |
|
1761 | |||
1711 | def magic_color_matches(self, text: str) -> List[str]: |
|
1762 | def magic_color_matches(self, text: str) -> List[str]: | |
1712 |
"""Match color schemes for %colors magic |
|
1763 | """Match color schemes for %colors magic. | |
|
1764 | ||||
|
1765 | DEPRECATED: Deprecated since 8.6. Use ``magic_color_matcher`` instead. | |||
|
1766 | """ | |||
1713 | texts = text.split() |
|
1767 | texts = text.split() | |
1714 | if text.endswith(' '): |
|
1768 | if text.endswith(' '): | |
1715 | # .split() strips off the trailing whitespace. Add '' back |
|
1769 | # .split() strips off the trailing whitespace. Add '' back | |
@@ -1731,8 +1785,8 b' class IPCompleter(Completer):' | |||||
1731 | ) |
|
1785 | ) | |
1732 | return { |
|
1786 | return { | |
1733 | "completions": matches, |
|
1787 | "completions": matches, | |
1734 |
# stati |
|
1788 | # static analysis should not suppress other matchers | |
1735 |
"suppress |
|
1789 | "suppress": False, | |
1736 | } |
|
1790 | } | |
1737 |
|
1791 | |||
1738 | def _jedi_matches( |
|
1792 | def _jedi_matches( | |
@@ -1755,6 +1809,8 b' class IPCompleter(Completer):' | |||||
1755 | ----- |
|
1809 | ----- | |
1756 | If ``IPCompleter.debug`` is ``True`` may return a :any:`_FakeJediCompletion` |
|
1810 | If ``IPCompleter.debug`` is ``True`` may return a :any:`_FakeJediCompletion` | |
1757 | object containing a string with the Jedi debug information attached. |
|
1811 | object containing a string with the Jedi debug information attached. | |
|
1812 | ||||
|
1813 | DEPRECATED: Deprecated since 8.6. Use ``_jedi_matcher`` instead. | |||
1758 | """ |
|
1814 | """ | |
1759 | namespaces = [self.namespace] |
|
1815 | namespaces = [self.namespace] | |
1760 | if self.global_namespace is not None: |
|
1816 | if self.global_namespace is not None: | |
@@ -1893,11 +1949,15 b' class IPCompleter(Completer):' | |||||
1893 |
|
1949 | |||
1894 | @context_matcher() |
|
1950 | @context_matcher() | |
1895 | def python_func_kw_matcher(self, context: CompletionContext) -> SimpleMatcherResult: |
|
1951 | def python_func_kw_matcher(self, context: CompletionContext) -> SimpleMatcherResult: | |
|
1952 | """Match named parameters (kwargs) of the last open function.""" | |||
1896 | matches = self.python_func_kw_matches(context.token) |
|
1953 | matches = self.python_func_kw_matches(context.token) | |
1897 | return _convert_matcher_v1_result_to_v2(matches, type="param") |
|
1954 | return _convert_matcher_v1_result_to_v2(matches, type="param") | |
1898 |
|
1955 | |||
1899 | def python_func_kw_matches(self, text): |
|
1956 | def python_func_kw_matches(self, text): | |
1900 |
"""Match named parameters (kwargs) of the last open function |
|
1957 | """Match named parameters (kwargs) of the last open function. | |
|
1958 | ||||
|
1959 | DEPRECATED: Deprecated since 8.6. Use ``magic_config_matcher`` instead. | |||
|
1960 | """ | |||
1901 |
|
1961 | |||
1902 | if "." in text: # a parameter cannot be dotted |
|
1962 | if "." in text: # a parameter cannot be dotted | |
1903 | return [] |
|
1963 | return [] | |
@@ -1994,6 +2054,7 b' class IPCompleter(Completer):' | |||||
1994 |
|
2054 | |||
1995 | @context_matcher() |
|
2055 | @context_matcher() | |
1996 | def dict_key_matcher(self, context: CompletionContext) -> SimpleMatcherResult: |
|
2056 | def dict_key_matcher(self, context: CompletionContext) -> SimpleMatcherResult: | |
|
2057 | """Match string keys in a dictionary, after e.g. ``foo[``.""" | |||
1997 | matches = self.dict_key_matches(context.token) |
|
2058 | matches = self.dict_key_matches(context.token) | |
1998 | return _convert_matcher_v1_result_to_v2( |
|
2059 | return _convert_matcher_v1_result_to_v2( | |
1999 | matches, type="dict key", suppress_if_matches=True |
|
2060 | matches, type="dict key", suppress_if_matches=True | |
@@ -2002,7 +2063,7 b' class IPCompleter(Completer):' | |||||
2002 | def dict_key_matches(self, text: str) -> List[str]: |
|
2063 | def dict_key_matches(self, text: str) -> List[str]: | |
2003 | """Match string keys in a dictionary, after e.g. ``foo[``. |
|
2064 | """Match string keys in a dictionary, after e.g. ``foo[``. | |
2004 |
|
2065 | |||
2005 |
DEPRECATED: Deprecated since 8. |
|
2066 | DEPRECATED: Deprecated since 8.6. Use `dict_key_matcher` instead. | |
2006 | """ |
|
2067 | """ | |
2007 |
|
2068 | |||
2008 | if self.__dict_key_regexps is not None: |
|
2069 | if self.__dict_key_regexps is not None: | |
@@ -2136,6 +2197,10 b' class IPCompleter(Completer):' | |||||
2136 |
|
2197 | |||
2137 | @context_matcher() |
|
2198 | @context_matcher() | |
2138 | def latex_name_matcher(self, context): |
|
2199 | def latex_name_matcher(self, context): | |
|
2200 | """Match Latex syntax for unicode characters. | |||
|
2201 | ||||
|
2202 | This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``Ξ±`` | |||
|
2203 | """ | |||
2139 | fragment, matches = self.latex_matches(context.token) |
|
2204 | fragment, matches = self.latex_matches(context.token) | |
2140 | return _convert_matcher_v1_result_to_v2( |
|
2205 | return _convert_matcher_v1_result_to_v2( | |
2141 | matches, type="latex", fragment=fragment, suppress_if_matches=True |
|
2206 | matches, type="latex", fragment=fragment, suppress_if_matches=True | |
@@ -2145,6 +2210,8 b' class IPCompleter(Completer):' | |||||
2145 | """Match Latex syntax for unicode characters. |
|
2210 | """Match Latex syntax for unicode characters. | |
2146 |
|
2211 | |||
2147 | This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``Ξ±`` |
|
2212 | This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``Ξ±`` | |
|
2213 | ||||
|
2214 | DEPRECATED: Deprecated since 8.6. Use `latex_matcher` instead. | |||
2148 | """ |
|
2215 | """ | |
2149 | slashpos = text.rfind('\\') |
|
2216 | slashpos = text.rfind('\\') | |
2150 | if slashpos > -1: |
|
2217 | if slashpos > -1: | |
@@ -2168,9 +2235,13 b' class IPCompleter(Completer):' | |||||
2168 | matches, type="<unknown>", suppress_if_matches=True |
|
2235 | matches, type="<unknown>", suppress_if_matches=True | |
2169 | ) |
|
2236 | ) | |
2170 | result["ordered"] = True |
|
2237 | result["ordered"] = True | |
|
2238 | result["do_not_suppress"] = {_get_matcher_id(self._jedi_matcher)} | |||
2171 | return result |
|
2239 | return result | |
2172 |
|
2240 | |||
2173 | def dispatch_custom_completer(self, text): |
|
2241 | def dispatch_custom_completer(self, text): | |
|
2242 | """ | |||
|
2243 | DEPRECATED: Deprecated since 8.6. Use `custom_completer_matcher` instead. | |||
|
2244 | """ | |||
2174 | if not self.custom_completers: |
|
2245 | if not self.custom_completers: | |
2175 | return |
|
2246 | return | |
2176 |
|
2247 | |||
@@ -2418,8 +2489,9 b' class IPCompleter(Completer):' | |||||
2418 | ) |
|
2489 | ) | |
2419 | container.append(completion) |
|
2490 | container.append(completion) | |
2420 |
|
2491 | |||
2421 | yield from self._deduplicate(ordered + self._sort(sortable)) |
|
2492 | yield from list(self._deduplicate(ordered + self._sort(sortable)))[ | |
2422 |
|
2493 | :MATCHES_LIMIT | ||
|
2494 | ] | |||
2423 |
|
2495 | |||
2424 | def complete(self, text=None, line_buffer=None, cursor_pos=None) -> Tuple[str, Sequence[str]]: |
|
2496 | def complete(self, text=None, line_buffer=None, cursor_pos=None) -> Tuple[str, Sequence[str]]: | |
2425 | """Find completions for the given text and line context. |
|
2497 | """Find completions for the given text and line context. | |
@@ -2490,6 +2562,8 b' class IPCompleter(Completer):' | |||||
2490 | for identifier, result in results.items(): |
|
2562 | for identifier, result in results.items(): | |
2491 | if identifier in skip_matchers: |
|
2563 | if identifier in skip_matchers: | |
2492 | continue |
|
2564 | continue | |
|
2565 | if not result["completions"]: | |||
|
2566 | continue | |||
2493 | if not most_recent_fragment: |
|
2567 | if not most_recent_fragment: | |
2494 | most_recent_fragment = result["matched_fragment"] |
|
2568 | most_recent_fragment = result["matched_fragment"] | |
2495 | if ( |
|
2569 | if ( | |
@@ -2578,34 +2652,42 b' class IPCompleter(Completer):' | |||||
2578 | cursor_position=cursor_pos, |
|
2652 | cursor_position=cursor_pos, | |
2579 | cursor_line=cursor_line, |
|
2653 | cursor_line=cursor_line, | |
2580 | token=text, |
|
2654 | token=text, | |
|
2655 | limit=MATCHES_LIMIT, | |||
2581 | ) |
|
2656 | ) | |
2582 |
|
2657 | |||
2583 | # Start with a clean slate of completions |
|
2658 | # Start with a clean slate of completions | |
2584 | results = {} |
|
2659 | results = {} | |
2585 |
|
2660 | |||
2586 | custom_completer_matcher_id = _get_matcher_id(self.custom_completer_matcher) |
|
|||
2587 | jedi_matcher_id = _get_matcher_id(self._jedi_matcher) |
|
2661 | jedi_matcher_id = _get_matcher_id(self._jedi_matcher) | |
2588 |
|
2662 | |||
2589 | for matcher in self.matchers: |
|
2663 | suppressed_matchers = set() | |
|
2664 | ||||
|
2665 | matchers = { | |||
|
2666 | _get_matcher_id(matcher): matcher | |||
|
2667 | for matcher in sorted( | |||
|
2668 | self.matchers, key=_get_matcher_priority, reverse=True | |||
|
2669 | ) | |||
|
2670 | } | |||
|
2671 | ||||
|
2672 | for matcher_id, matcher in matchers.items(): | |||
2590 | api_version = _get_matcher_api_version(matcher) |
|
2673 | api_version = _get_matcher_api_version(matcher) | |
2591 | matcher_id = _get_matcher_id(matcher) |
|
2674 | matcher_id = _get_matcher_id(matcher) | |
2592 |
|
2675 | |||
|
2676 | if matcher_id in self.disable_matchers: | |||
|
2677 | continue | |||
|
2678 | ||||
2593 | if matcher_id in results: |
|
2679 | if matcher_id in results: | |
2594 | warnings.warn(f"Duplicate matcher ID: {matcher_id}.") |
|
2680 | warnings.warn(f"Duplicate matcher ID: {matcher_id}.") | |
2595 |
|
2681 | |||
|
2682 | if matcher_id in suppressed_matchers: | |||
|
2683 | continue | |||
|
2684 | ||||
2596 | try: |
|
2685 | try: | |
2597 | if api_version == 1: |
|
2686 | if api_version == 1: | |
2598 | result = _convert_matcher_v1_result_to_v2( |
|
2687 | result = _convert_matcher_v1_result_to_v2( | |
2599 | matcher(text), type=_UNKNOWN_TYPE |
|
2688 | matcher(text), type=_UNKNOWN_TYPE | |
2600 | ) |
|
2689 | ) | |
2601 | elif api_version == 2: |
|
2690 | elif api_version == 2: | |
2602 | # TODO: MATCHES_LIMIT was used inconsistently in previous version |
|
|||
2603 | # (applied individually to latex/unicode and magic arguments matcher, |
|
|||
2604 | # but not Jedi, paths, magics, etc). Jedi did not have a limit here at |
|
|||
2605 | # all, but others had a total limit (retained in `_deduplicate_and_sort`). |
|
|||
2606 | # 1) Was that deliberate or an omission? |
|
|||
2607 | # 2) Should we include the limit in the API v2 signature to allow |
|
|||
2608 | # more expensive matchers to return early? |
|
|||
2609 | result = cast(matcher, MatcherAPIv2)(context) |
|
2691 | result = cast(matcher, MatcherAPIv2)(context) | |
2610 | else: |
|
2692 | else: | |
2611 | raise ValueError(f"Unsupported API version {api_version}") |
|
2693 | raise ValueError(f"Unsupported API version {api_version}") | |
@@ -2618,27 +2700,31 b' class IPCompleter(Completer):' | |||||
2618 | # set default value for matched fragment if suffix was not selected. |
|
2700 | # set default value for matched fragment if suffix was not selected. | |
2619 | result["matched_fragment"] = result.get("matched_fragment", context.token) |
|
2701 | result["matched_fragment"] = result.get("matched_fragment", context.token) | |
2620 |
|
2702 | |||
2621 | suppression_recommended = result.get("suppress_others", False) |
|
2703 | if not suppressed_matchers: | |
|
2704 | suppression_recommended = result.get("suppress", False) | |||
2622 |
|
2705 | |||
2623 | should_suppress = ( |
|
2706 | should_suppress = ( | |
2624 | self.suppress_competing_matchers is True |
|
2707 | self.suppress_competing_matchers is True | |
2625 | or suppression_recommended |
|
2708 | or suppression_recommended | |
2626 | or ( |
|
2709 | or ( | |
2627 | isinstance(self.suppress_competing_matchers, dict) |
|
2710 | isinstance(self.suppress_competing_matchers, dict) | |
2628 | and self.suppress_competing_matchers[matcher_id] |
|
2711 | and self.suppress_competing_matchers[matcher_id] | |
2629 | ) |
|
2712 | ) | |
2630 | ) and len(result["completions"]) |
|
2713 | ) and len(result["completions"]) | |
2631 |
|
2714 | |||
2632 | if should_suppress: |
|
2715 | if should_suppress: | |
2633 | new_results = {matcher_id: result} |
|
2716 | suppression_exceptions = result.get("do_not_suppress", set()) | |
2634 |
|
|
2717 | try: | |
2635 | matcher_id == custom_completer_matcher_id |
|
2718 | to_suppress = set(suppression_recommended) | |
2636 | and jedi_matcher_id in results |
|
2719 | except TypeError: | |
2637 | ): |
|
2720 | to_suppress = set(matchers) | |
2638 | # custom completer does not suppress Jedi (this may change in future versions). |
|
2721 | suppressed_matchers = to_suppress - suppression_exceptions | |
2639 | new_results[jedi_matcher_id] = results[jedi_matcher_id] |
|
2722 | ||
2640 |
results = |
|
2723 | new_results = {} | |
2641 | break |
|
2724 | for previous_matcher_id, previous_result in results.items(): | |
|
2725 | if previous_matcher_id not in suppressed_matchers: | |||
|
2726 | new_results[previous_matcher_id] = previous_result | |||
|
2727 | results = new_results | |||
2642 |
|
2728 | |||
2643 | results[matcher_id] = result |
|
2729 | results[matcher_id] = result | |
2644 |
|
2730 | |||
@@ -2676,6 +2762,7 b' class IPCompleter(Completer):' | |||||
2676 |
|
2762 | |||
2677 | @context_matcher() |
|
2763 | @context_matcher() | |
2678 | def fwd_unicode_matcher(self, context): |
|
2764 | def fwd_unicode_matcher(self, context): | |
|
2765 | """Same as ``fwd_unicode_match``, but adopted to new Matcher API.""" | |||
2679 | fragment, matches = self.latex_matches(context.token) |
|
2766 | fragment, matches = self.latex_matches(context.token) | |
2680 | return _convert_matcher_v1_result_to_v2( |
|
2767 | return _convert_matcher_v1_result_to_v2( | |
2681 | matches, type="unicode", fragment=fragment, suppress_if_matches=True |
|
2768 | matches, type="unicode", fragment=fragment, suppress_if_matches=True | |
@@ -2693,6 +2780,8 b' class IPCompleter(Completer):' | |||||
2693 | At tuple with: |
|
2780 | At tuple with: | |
2694 | - matched text (empty if no matches) |
|
2781 | - matched text (empty if no matches) | |
2695 | - list of potential completions, empty tuple otherwise) |
|
2782 | - list of potential completions, empty tuple otherwise) | |
|
2783 | ||||
|
2784 | DEPRECATED: Deprecated since 8.6. Use `fwd_unicode_matcher` instead. | |||
2696 | """ |
|
2785 | """ | |
2697 | # TODO: self.unicode_names is here a list we traverse each time with ~100k elements. |
|
2786 | # TODO: self.unicode_names is here a list we traverse each time with ~100k elements. | |
2698 | # We could do a faster match using a Trie. |
|
2787 | # We could do a faster match using a Trie. |
@@ -105,7 +105,7 b' class ConfigMagics(Magics):' | |||||
105 | Whether to merge completion results into a single list |
|
105 | Whether to merge completion results into a single list | |
106 | If False, only the completion results from the first non-empty |
|
106 | If False, only the completion results from the first non-empty | |
107 | completer will be returned. |
|
107 | completer will be returned. | |
108 |
As of version 8. |
|
108 | As of version 8.6.0, setting the value to ``False`` is an alias for: | |
109 | ``IPCompleter.suppress_competing_matchers = True.``. |
|
109 | ``IPCompleter.suppress_competing_matchers = True.``. | |
110 | Current: True |
|
110 | Current: True | |
111 | IPCompleter.omit__names=<Enum> |
|
111 | IPCompleter.omit__names=<Enum> | |
@@ -123,7 +123,7 b' class ConfigMagics(Magics):' | |||||
123 | Template for path at which to output profile data for completions. |
|
123 | Template for path at which to output profile data for completions. | |
124 | Current: '.completion_profiles' |
|
124 | Current: '.completion_profiles' | |
125 | IPCompleter.suppress_competing_matchers=<Union> |
|
125 | IPCompleter.suppress_competing_matchers=<Union> | |
126 |
Whether to suppress completions from other |
|
126 | Whether to suppress completions from other *Matchers*. | |
127 | When set to ``None`` (default) the matchers will attempt to auto-detect |
|
127 | When set to ``None`` (default) the matchers will attempt to auto-detect | |
128 | whether suppression of other matchers is desirable. For example, at the |
|
128 | whether suppression of other matchers is desirable. For example, at the | |
129 | beginning of a line followed by `%` we expect a magic completion to be the |
|
129 | beginning of a line followed by `%` we expect a magic completion to be the | |
@@ -136,9 +136,9 b' class ConfigMagics(Magics):' | |||||
136 | False}``. |
|
136 | False}``. | |
137 | Set ``IPCompleter.suppress_competing_matchers = True`` to limit completions |
|
137 | Set ``IPCompleter.suppress_competing_matchers = True`` to limit completions | |
138 | to the set of matchers with the highest priority; this is equivalent to |
|
138 | to the set of matchers with the highest priority; this is equivalent to | |
139 |
``IPCompleter.merge_completions`` and |
|
139 | ``IPCompleter.merge_completions`` and can be beneficial for performance, but | |
140 |
|
|
140 | will sometimes omit relevant candidates from matchers further down the | |
141 |
|
|
141 | priority list. | |
142 | Current: False |
|
142 | Current: False | |
143 | IPCompleter.use_jedi=<Bool> |
|
143 | IPCompleter.use_jedi=<Bool> | |
144 | Experimental: Use Jedi to generate autocompletions. Default to True if jedi |
|
144 | Experimental: Use Jedi to generate autocompletions. Default to True if jedi |
@@ -24,6 +24,9 b' from IPython.core.completer import (' | |||||
24 | provisionalcompleter, |
|
24 | provisionalcompleter, | |
25 | match_dict_keys, |
|
25 | match_dict_keys, | |
26 | _deduplicate_completions, |
|
26 | _deduplicate_completions, | |
|
27 | completion_matcher, | |||
|
28 | SimpleCompletion, | |||
|
29 | CompletionContext, | |||
27 | ) |
|
30 | ) | |
28 |
|
31 | |||
29 | # ----------------------------------------------------------------------------- |
|
32 | # ----------------------------------------------------------------------------- | |
@@ -109,6 +112,16 b' def greedy_completion():' | |||||
109 | ip.Completer.greedy = greedy_original |
|
112 | ip.Completer.greedy = greedy_original | |
110 |
|
113 | |||
111 |
|
114 | |||
|
115 | @contextmanager | |||
|
116 | def custom_matchers(matchers): | |||
|
117 | ip = get_ipython() | |||
|
118 | try: | |||
|
119 | ip.Completer.custom_matchers.extend(matchers) | |||
|
120 | yield | |||
|
121 | finally: | |||
|
122 | ip.Completer.custom_matchers.clear() | |||
|
123 | ||||
|
124 | ||||
112 | def test_protect_filename(): |
|
125 | def test_protect_filename(): | |
113 | if sys.platform == "win32": |
|
126 | if sys.platform == "win32": | |
114 | pairs = [ |
|
127 | pairs = [ | |
@@ -1281,3 +1294,97 b' class TestCompleter(unittest.TestCase):' | |||||
1281 | completions = completer.completions(text, len(text)) |
|
1294 | completions = completer.completions(text, len(text)) | |
1282 | for c in completions: |
|
1295 | for c in completions: | |
1283 | self.assertEqual(c.text[0], "%") |
|
1296 | self.assertEqual(c.text[0], "%") | |
|
1297 | ||||
|
1298 | def test_matcher_suppression(self): | |||
|
1299 | @completion_matcher(identifier="a_matcher") | |||
|
1300 | def a_matcher(text): | |||
|
1301 | return ["completion_a"] | |||
|
1302 | ||||
|
1303 | @completion_matcher(identifier="b_matcher", api_version=2) | |||
|
1304 | def b_matcher(context: CompletionContext): | |||
|
1305 | text = context.token | |||
|
1306 | result = {"completions": [SimpleCompletion("completion_b")]} | |||
|
1307 | ||||
|
1308 | if text == "suppress c": | |||
|
1309 | result["suppress"] = {"c_matcher"} | |||
|
1310 | ||||
|
1311 | if text.startswith("suppress all"): | |||
|
1312 | result["suppress"] = True | |||
|
1313 | if text == "suppress all but c": | |||
|
1314 | result["do_not_suppress"] = {"c_matcher"} | |||
|
1315 | if text == "suppress all but a": | |||
|
1316 | result["do_not_suppress"] = {"a_matcher"} | |||
|
1317 | ||||
|
1318 | return result | |||
|
1319 | ||||
|
1320 | @completion_matcher(identifier="c_matcher") | |||
|
1321 | def c_matcher(text): | |||
|
1322 | return ["completion_c"] | |||
|
1323 | ||||
|
1324 | with custom_matchers([a_matcher, b_matcher, c_matcher]): | |||
|
1325 | ip = get_ipython() | |||
|
1326 | c = ip.Completer | |||
|
1327 | ||||
|
1328 | def _(text, expected): | |||
|
1329 | with provisionalcompleter(): | |||
|
1330 | c.use_jedi = False | |||
|
1331 | s, matches = c.complete(text) | |||
|
1332 | self.assertEqual(expected, matches) | |||
|
1333 | ||||
|
1334 | _("do not suppress", ["completion_a", "completion_b", "completion_c"]) | |||
|
1335 | _("suppress all", ["completion_b"]) | |||
|
1336 | _("suppress all but a", ["completion_a", "completion_b"]) | |||
|
1337 | _("suppress all but c", ["completion_b", "completion_c"]) | |||
|
1338 | ||||
|
1339 | def test_matcher_disabling(self): | |||
|
1340 | @completion_matcher(identifier="a_matcher") | |||
|
1341 | def a_matcher(text): | |||
|
1342 | return ["completion_a"] | |||
|
1343 | ||||
|
1344 | @completion_matcher(identifier="b_matcher") | |||
|
1345 | def b_matcher(text): | |||
|
1346 | return ["completion_b"] | |||
|
1347 | ||||
|
1348 | def _(expected): | |||
|
1349 | with provisionalcompleter(): | |||
|
1350 | c.use_jedi = False | |||
|
1351 | s, matches = c.complete("completion_") | |||
|
1352 | self.assertEqual(expected, matches) | |||
|
1353 | ||||
|
1354 | with custom_matchers([a_matcher, b_matcher]): | |||
|
1355 | ip = get_ipython() | |||
|
1356 | c = ip.Completer | |||
|
1357 | ||||
|
1358 | _(["completion_a", "completion_b"]) | |||
|
1359 | ||||
|
1360 | cfg = Config() | |||
|
1361 | cfg.IPCompleter.disable_matchers = ["b_matcher"] | |||
|
1362 | c.update_config(cfg) | |||
|
1363 | ||||
|
1364 | _(["completion_a"]) | |||
|
1365 | ||||
|
1366 | cfg.IPCompleter.disable_matchers = [] | |||
|
1367 | c.update_config(cfg) | |||
|
1368 | ||||
|
1369 | def test_matcher_priority(self): | |||
|
1370 | @completion_matcher(identifier="a_matcher", priority=0, api_version=2) | |||
|
1371 | def a_matcher(text): | |||
|
1372 | return {"completions": [SimpleCompletion("completion_a")], "suppress": True} | |||
|
1373 | ||||
|
1374 | @completion_matcher(identifier="b_matcher", priority=2, api_version=2) | |||
|
1375 | def b_matcher(text): | |||
|
1376 | return {"completions": [SimpleCompletion("completion_b")], "suppress": True} | |||
|
1377 | ||||
|
1378 | def _(expected): | |||
|
1379 | with provisionalcompleter(): | |||
|
1380 | c.use_jedi = False | |||
|
1381 | s, matches = c.complete("completion_") | |||
|
1382 | self.assertEqual(expected, matches) | |||
|
1383 | ||||
|
1384 | with custom_matchers([a_matcher, b_matcher]): | |||
|
1385 | ip = get_ipython() | |||
|
1386 | c = ip.Completer | |||
|
1387 | ||||
|
1388 | _(["completion_b"]) | |||
|
1389 | a_matcher.matcher_priority = 3 | |||
|
1390 | _(["completion_a"]) |
General Comments 0
You need to be logged in to leave comments.
Login now