##// END OF EJS Templates
Merge pull request #10285 from Carreau/deduplicate-completions...
Fernando Perez -
r23359:9f622df4 merge
parent child Browse files
Show More
@@ -344,11 +344,52 b' class Completion:'
344 def __hash__(self):
344 def __hash__(self):
345 return hash((self.start, self.end, self.text))
345 return hash((self.start, self.end, self.text))
346
346
347
347 _IC = Iterator[Completion]
348 _IC = Iterator[Completion]
348
349
349 def rectify_completions(text:str, completion:_IC, *, _debug=False)->_IC:
350
351 def _deduplicate_completions(text: str, completions: _IC)-> _IC:
352 """
353 Deduplicate a set of completions.
354
355 .. warning:: Unstable
356
357 This function is unstable, API may change without warning.
358
359 Parameters
360 ----------
361 text: str
362 text that should be completed.
363 completions: Iterator[Completion]
364 iterator over the completions to deduplicate
365
366
367 Completions coming from multiple sources, may be different but end up having
368 the same effect when applied to ``text``. If this is the case, this will
369 consider completions as equal and only emit the first encountered.
370
371 Not folded in `completions()` yet for debugging purpose, and to detect when
372 the IPython completer does return things that Jedi does not, but should be
373 at some point.
374 """
375 completions = list(completions)
376 if not completions:
377 return
378
379 new_start = min(c.start for c in completions)
380 new_end = max(c.end for c in completions)
381
382 seen = set()
383 for c in completions:
384 new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
385 if new_text not in seen:
386 yield c
387 seen.add(new_text)
388
389
390 def rectify_completions(text: str, completions: _IC, *, _debug=False)->_IC:
350 """
391 """
351 Rectify a set of completion to all have the same ``start`` and ``end``
392 Rectify a set of completions to all have the same ``start`` and ``end``
352
393
353 .. warning:: Unstable
394 .. warning:: Unstable
354
395
@@ -359,7 +400,7 b' def rectify_completions(text:str, completion:_IC, *, _debug=False)->_IC:'
359 ----------
400 ----------
360 text: str
401 text: str
361 text that should be completed.
402 text that should be completed.
362 completion: Iterator[Completion]
403 completions: Iterator[Completion]
363 iterator over the completions to rectify
404 iterator over the completions to rectify
364
405
365
406
@@ -1510,22 +1551,17 b' class IPCompleter(Completer):'
1510 fake Completion token to distinguish completion returned by Jedi
1551 fake Completion token to distinguish completion returned by Jedi
1511 and usual IPython completion.
1552 and usual IPython completion.
1512
1553
1554 .. note::
1555
1556 Completions are not completely deduplicated yet. If identical
1557 completions are coming from different sources this function does not
1558 ensure that each completion object will only be present once.
1513 """
1559 """
1514 warnings.warn("_complete is a provisional API (as of IPython 6.0). "
1560 warnings.warn("_complete is a provisional API (as of IPython 6.0). "
1515 "It may change without warnings. "
1561 "It may change without warnings. "
1516 "Use in corresponding context manager.",
1562 "Use in corresponding context manager.",
1517 category=ProvisionalCompleterWarning, stacklevel=2)
1563 category=ProvisionalCompleterWarning, stacklevel=2)
1518
1564
1519 # Possible Improvements / Known limitation
1520 ##########################################
1521 # Completions may be identical even if they have different ranges and
1522 # text. For example:
1523 # >>> a=1
1524 # >>> a.<tab>
1525 # May returns:
1526 # - `a.real` from 0 to 2
1527 # - `.real` from 1 to 2
1528 # the current code does not (yet) check for such equivalence
1529 seen = set()
1565 seen = set()
1530 for c in self._completions(text, offset, _timeout=self.jedi_compute_type_timeout/1000):
1566 for c in self._completions(text, offset, _timeout=self.jedi_compute_type_timeout/1000):
1531 if c and (c in seen):
1567 if c and (c in seen):
@@ -6,6 +6,7 b''
6
6
7 import os
7 import os
8 import sys
8 import sys
9 import textwrap
9 import unittest
10 import unittest
10
11
11 from contextlib import contextmanager
12 from contextlib import contextmanager
@@ -20,7 +21,8 b' from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory'
20 from IPython.utils.generics import complete_object
21 from IPython.utils.generics import complete_object
21 from IPython.testing import decorators as dec
22 from IPython.testing import decorators as dec
22
23
23 from IPython.core.completer import Completion, provisionalcompleter, match_dict_keys
24 from IPython.core.completer import (
25 Completion, provisionalcompleter, match_dict_keys, _deduplicate_completions)
24 from nose.tools import assert_in, assert_not_in
26 from nose.tools import assert_in, assert_not_in
25
27
26 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
@@ -302,6 +304,21 b' def test_jedi():'
302
304
303 yield _test_not_complete, 'does not mix types', 'a=(1,"foo");a[0].', 'capitalize'
305 yield _test_not_complete, 'does not mix types', 'a=(1,"foo");a[0].', 'capitalize'
304
306
307 def test_deduplicate_completions():
308 """
309 Test that completions are correctly deduplicated (even if ranges are not the same)
310 """
311 ip = get_ipython()
312 ip.ex(textwrap.dedent('''
313 class Z:
314 zoo = 1
315 '''))
316 with provisionalcompleter():
317 l = list(_deduplicate_completions('Z.z', ip.Completer.completions('Z.z', 3)))
318
319 assert len(l) == 1, 'Completions (Z.z<tab>) correctly deduplicate: %s ' % l
320 assert l[0].text == 'zoo' # and not `it.accumulate`
321
305
322
306 def test_greedy_completions():
323 def test_greedy_completions():
307 """
324 """
@@ -10,7 +10,9 b' not to be used outside IPython.'
10 import unicodedata
10 import unicodedata
11 from wcwidth import wcwidth
11 from wcwidth import wcwidth
12
12
13 from IPython.core.completer import IPCompleter, provisionalcompleter, rectify_completions, cursor_to_position
13 from IPython.core.completer import (
14 IPCompleter, provisionalcompleter, rectify_completions, cursor_to_position,
15 _deduplicate_completions)
14 from prompt_toolkit.completion import Completer, Completion
16 from prompt_toolkit.completion import Completer, Completion
15 from prompt_toolkit.layout.lexers import Lexer
17 from prompt_toolkit.layout.lexers import Lexer
16 from prompt_toolkit.layout.lexers import PygmentsLexer
18 from prompt_toolkit.layout.lexers import PygmentsLexer
@@ -61,8 +63,8 b' class IPythonPTCompleter(Completer):'
61 Private equivalent of get_completions() use only for unit_testing.
63 Private equivalent of get_completions() use only for unit_testing.
62 """
64 """
63 debug = getattr(ipyc, 'debug', False)
65 debug = getattr(ipyc, 'debug', False)
64 completions = rectify_completions(
66 completions = _deduplicate_completions(
65 body, ipyc.completions(body, offset), _debug=debug)
67 body, ipyc.completions(body, offset))
66 for c in completions:
68 for c in completions:
67 if not c.text:
69 if not c.text:
68 # Guard against completion machinery giving us an empty string.
70 # Guard against completion machinery giving us an empty string.
General Comments 0
You need to be logged in to leave comments. Login now