| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 3a1e177 commit 1a0aa07
7 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -48,17 +48,10 @@ | |||
| 48 | 48 | SUBSTRING = 'substring' | |
| 49 | 49 | FUZZY = 'fuzzy' | |
| 50 | 50 | ||
| 51 | - class Autocomplete(rlcompleter.Completer): | ||
| 52 | - def __init__(self, namespace, config): | ||
| 53 | - self.namespace = namespace | ||
| 54 | - self.config = config | ||
| 55 | - def complete(self, cw, _): | ||
| 56 | - self.matches = complete(cw, namespace=self.namespace, config=self.config) | ||
| 57 | - | ||
| 58 | 51 | def complete(text, namespace=None, config=None): | |
| 59 | 52 | """Return list of matches """ | |
| 60 | 53 | if namespace is None: | |
| 61 | - namespace = __main__.__dict__ | ||
| 54 | + namespace = __main__.__dict__ #TODO figure out if this __main__ still makes sense | ||
| 62 | 55 | ||
| 63 | 56 | if hasattr(config, 'autocomplete_mode'): | |
| 64 | 57 | autocomplete_mode = config.autocomplete_mode | |
@@ -182,37 +175,40 @@ def filename_matches(cs): | |||
| 182 | 175 | if cs.startswith('~'): | |
| 183 | 176 | filename = username + filename[len(user_dir):] | |
| 184 | 177 | matches.append(filename) | |
| 185 | - return cs | ||
| 178 | + return matches | ||
| 186 | 179 | ||
| 187 | - def find_matches(cursor_offset, current_line, locals_, current_string_callback, completer, magic_methods, argspec): | ||
| 180 | + def find_matches(cursor_offset, current_line, locals_, argspec, config, magic_methods): | ||
| 188 | 181 | """Returns a list of matches and function to use for replacing words on tab""" | |
| 189 | 182 | ||
| 183 | + #TODO use the smarter current_string() in Repl that knows about the buffer | ||
| 184 | + #TODO don't pass in config, pass in the settings themselves | ||
| 185 | + #TODO if importcompletion returns None, that means short circuit return, not | ||
| 186 | + # try something else | ||
| 190 | 187 | if line.current_string(cursor_offset, current_line): | |
| 191 | - matches = filename_matches(line.current_string(cursor_offset, current_line)) | ||
| 188 | + matches = filename_matches(line.current_string(cursor_offset, current_line)[2]) | ||
| 192 | 189 | return matches, line.current_string | |
| 193 | 190 | ||
| 194 | 191 | if line.current_word(cursor_offset, current_line) is None: | |
| 195 | 192 | return [], None | |
| 196 | 193 | ||
| 197 | 194 | matches = importcompletion.complete(cursor_offset, current_line) | |
| 198 | - return matches, line.current_word | ||
| 195 | + if matches: | ||
| 196 | + return matches, line.current_word | ||
| 199 | 197 | ||
| 200 | 198 | cw = line.current_word(cursor_offset, current_line)[2] | |
| 201 | 199 | ||
| 202 | 200 | try: | |
| 203 | - completer.complete(cw, 0) | ||
| 204 | - #except Exception: | ||
| 205 | - # # This sucks, but it's either that or list all the exceptions that could | ||
| 206 | - # # possibly be raised here, so if anyone wants to do that, feel free to send me | ||
| 207 | - # # a patch. XXX: Make sure you raise here if you're debugging the completion | ||
| 208 | - # # stuff ! | ||
| 209 | - # e = True | ||
| 210 | - #else: | ||
| 201 | + matches = complete(cw, namespace=locals_, config=config) | ||
| 202 | + except Exception: | ||
| 203 | + # This sucks, but it's either that or list all the exceptions that could | ||
| 204 | + # possibly be raised here, so if anyone wants to do that, feel free to send me | ||
| 205 | + # a patch. XXX: Make sure you raise here if you're debugging the completion | ||
| 206 | + # stuff ! | ||
| 207 | + e = True | ||
| 208 | + raise | ||
| 209 | + else: | ||
| 211 | 210 | e = False | |
| 212 | - matches = completer.matches | ||
| 213 | 211 | matches.extend(magic_methods(cw)) | |
| 214 | - except KeyboardInterrupt: | ||
| 215 | - pass | ||
| 216 | 212 | ||
| 217 | 213 | if not e and argspec: | |
| 218 | 214 | matches.extend(name + '=' for name in argspec[1][0] | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -459,7 +459,7 @@ def complete(self, tab=False): | |||
| 459 | 459 | if self.list_win_visible and not self.config.auto_display_list: | |
| 460 | 460 | self.scr.touchwin() | |
| 461 | 461 | self.list_win_visible = False | |
| 462 | - self.matches_iter.update() | ||
| 462 | + self.matches_iter.clear() | ||
| 463 | 463 | return | |
| 464 | 464 | ||
| 465 | 465 | if self.config.auto_display_list or tab: | |
@@ -1484,43 +1484,42 @@ def tab(self, back=False): | |||
| 1484 | 1484 | cseq = os.path.commonprefix(seq) | |
| 1485 | 1485 | ||
| 1486 | 1486 | if cseq and mode != autocomplete.FUZZY: | |
| 1487 | + print 'doing cseq' | ||
| 1487 | 1488 | expanded_string = cseq[len(cw):] | |
| 1488 | - self.s += expanded_string | ||
| 1489 | - expanded = bool(expanded_string) | ||
| 1489 | + expanded = bool(expanded_string) #TODO move this logic below to matches_iter | ||
| 1490 | + _, self.s = self.matches_iter.substitute(self.matches_iter.current_word + expanded_string) | ||
| 1490 | 1491 | self.print_line(self.s) | |
| 1491 | 1492 | if len(self.matches) == 1 and self.config.auto_display_list: | |
| 1492 | 1493 | self.scr.touchwin() | |
| 1493 | 1494 | if expanded: | |
| 1494 | - self.matches_iter.update(cseq, self.matches) | ||
| 1495 | + self.matches_iter.update(len(self.s) - self.cpos, | ||
| 1496 | + self.s, | ||
| 1497 | + self.matches) | ||
| 1495 | 1498 | else: | |
| 1496 | 1499 | expanded = False | |
| 1497 | 1500 | ||
| 1498 | 1501 | # 4. swap current word for a match list item | |
| 1499 | 1502 | if not expanded and self.matches: | |
| 1500 | - # reset s if this is the nth result | ||
| 1501 | - if self.matches_iter: | ||
| 1502 | - self.s = self.s[:-len(self.matches_iter.current())] + cw | ||
| 1503 | + print 'doing swap' | ||
| 1503 | 1504 | ||
| 1504 | 1505 | current_match = back and self.matches_iter.previous() \ | |
| 1505 | 1506 | or self.matches_iter.next() | |
| 1506 | 1507 | ||
| 1507 | - # update s with the new match | ||
| 1508 | - if current_match: | ||
| 1509 | - try: | ||
| 1510 | - self.show_list(self.matches, self.argspec, current_match) | ||
| 1511 | - except curses.error: | ||
| 1512 | - # XXX: This is a massive hack, it will go away when I get | ||
| 1513 | - # cusswords into a good enough state that we can start | ||
| 1514 | - # using it. | ||
| 1515 | - self.list_win.border() | ||
| 1516 | - self.list_win.refresh() | ||
| 1517 | - | ||
| 1518 | - if self.config.autocomplete_mode == autocomplete.SIMPLE: | ||
| 1519 | - self.s += current_match[len(cw):] | ||
| 1520 | - else: | ||
| 1521 | - self.s = self.s[:-len(cw)] + current_match | ||
| 1508 | + try: | ||
| 1509 | + self.show_list(self.matches, self.argspec, current_match) | ||
| 1510 | + except curses.error: | ||
| 1511 | + # XXX: This is a massive hack, it will go away when I get | ||
| 1512 | + # cusswords into a good enough state that we can start | ||
| 1513 | + # using it. | ||
| 1514 | + self.list_win.border() | ||
| 1515 | + self.list_win.refresh() | ||
| 1516 | + | ||
| 1517 | + if self.config.autocomplete_mode == autocomplete.SIMPLE: | ||
| 1518 | + _, self.s = self.matches_iter.cur_line() | ||
| 1519 | + else: | ||
| 1520 | + self.s = self.s[:-len(cw)] + current_match | ||
| 1522 | 1521 | ||
| 1523 | - self.print_line(self.s, True) | ||
| 1522 | + self.print_line(self.s, True) | ||
| 1524 | 1523 | return True | |
| 1525 | 1524 | ||
| 1526 | 1525 | def undo(self, n=1): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -827,6 +827,7 @@ def paint(self, about_to_exit=False, user_quit=False): | |||
| 827 | 827 | logging.debug('cursor pos: %r', (cursor_row, cursor_column)) | |
| 828 | 828 | return arr, (cursor_row, cursor_column) | |
| 829 | 829 | ||
| 830 | + | ||
| 830 | 831 | @contextlib.contextmanager | |
| 831 | 832 | def in_paste_mode(self): | |
| 832 | 833 | orig_value = self.paste_mode | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -42,10 +42,10 @@ def current_string(cursor_offset, line): | |||
| 42 | 42 | ||
| 43 | 43 | Weaker than bpython.Repl's current_string, because that checks that a string is a string | |
| 44 | 44 | based on previous lines in the buffer""" | |
| 45 | - matches = list(re.finditer('''(?P<open>(?:""")|"|(?:''\')|')(.*?)(?P=open)''', line)) | ||
| 46 | - for m in matches: | ||
| 47 | - if m.start(2) <= cursor_offset and m.end(2) >= cursor_offset: | ||
| 48 | - return m.start(2), m.end(2), m.group(2) | ||
| 45 | + for m in re.finditer('''(?P<open>(?:""")|"|(?:''\')|')(?:((?P<closed>.*?)(?P=open))|(?P<unclosed>.*))''', line): | ||
| 46 | + i = 3 if m.group(3) else 4 | ||
| 47 | + if m.start(i) <= cursor_offset and m.end(i) >= cursor_offset: | ||
| 48 | + return m.start(i), m.end(i), m.group(i) | ||
| 49 | 49 | return None | |
| 50 | 50 | ||
| 51 | 51 | def current_object(cursor_offset, line): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -50,7 +50,7 @@ | |||
| 50 | 50 | from bpython._py3compat import PythonLexer, py3 | |
| 51 | 51 | from bpython.formatter import Parenthesis | |
| 52 | 52 | from bpython.translations import _ | |
| 53 | - from bpython.autocomplete import Autocomplete | ||
| 53 | + import bpython.autocomplete as autocomplete | ||
| 54 | 54 | ||
| 55 | 55 | ||
| 56 | 56 | # Needed for special handling of __abstractmethods__ | |
@@ -280,6 +280,9 @@ def __init__(self, current_word='', matches=[]): | |||
| 280 | 280 | self.current_word = current_word | |
| 281 | 281 | self.matches = list(matches) | |
| 282 | 282 | self.index = -1 | |
| 283 | + self.orig_cursor_offset = None | ||
| 284 | + self.orig_line = None | ||
| 285 | + self.replacer = None | ||
| 283 | 286 | ||
| 284 | 287 | def __nonzero__(self): | |
| 285 | 288 | return self.index != -1 | |
@@ -303,11 +306,33 @@ def previous(self): | |||
| 303 | 306 | ||
| 304 | 307 | return self.matches[self.index] | |
| 305 | 308 | ||
| 306 | - def update(self, current_word='', matches=[]): | ||
| 307 | - if current_word != self.current_word: | ||
| 308 | - self.current_word = current_word | ||
| 309 | - self.matches = list(matches) | ||
| 309 | + def cur_line(self): | ||
| 310 | + """Returns a cursor offset and line pair with the current substitution made""" | ||
| 311 | + return self.substitute(self.current()) | ||
| 312 | + | ||
| 313 | + def substitute(self, match): | ||
| 314 | + if match.startswith("'"): | ||
| 315 | + raise ValueError(match) | ||
| 316 | + start, end, word = self.replacer(self.orig_cursor_offset, self.orig_line) | ||
| 317 | + result = start + len(match), self.orig_line[:start] + match + self.orig_line[end:] | ||
| 318 | + return result | ||
| 319 | + | ||
| 320 | + def update(self, cursor_offset, current_line, matches, replacer=None): | ||
| 321 | + if cursor_offset != self.orig_cursor_offset or current_line != self.orig_line: | ||
| 322 | + if replacer is not None: | ||
| 323 | + self.replacer = replacer | ||
| 324 | + if matches and self.replacer is not None: | ||
| 325 | + self.start, self.end, self.current_word = self.replacer(cursor_offset, current_line) | ||
| 326 | + self.orig_cursor_offset = cursor_offset | ||
| 327 | + self.orig_line = current_line | ||
| 310 | 328 | self.index = -1 | |
| 329 | + self.matches = list(matches) | ||
| 330 | + | ||
| 331 | + def clear(self): | ||
| 332 | + self.cursor_offset = -1 | ||
| 333 | + self.current_line = '' | ||
| 334 | + self.current_word = '' | ||
| 335 | + self.replacer = None | ||
| 311 | 336 | ||
| 312 | 337 | ||
| 313 | 338 | class Interaction(object): | |
@@ -378,7 +403,6 @@ def __init__(self, interp, config): | |||
| 378 | 403 | self.s_hist = [] | |
| 379 | 404 | self.history = [] | |
| 380 | 405 | self.evaluating = False | |
| 381 | - self.completer = Autocomplete(self.interp.locals, config) | ||
| 382 | 406 | self.matches = [] | |
| 383 | 407 | self.matches_iter = MatchesIterator() | |
| 384 | 408 | self.argspec = None | |
@@ -580,83 +604,30 @@ def complete(self, tab=False): | |||
| 580 | 604 | (via the inspect module) and bang that on top of the completions too. | |
| 581 | 605 | The return value is whether the list_win is visible or not.""" | |
| 582 | 606 | ||
| 583 | - #TODO keep factoring out parts of complete | ||
| 584 | - # so we can reuse code when we reimplement complete | ||
| 585 | - # in bpython.curtsiesfrontend.repl | ||
| 586 | - # Maybe move things to autocomplete instead of here? | ||
| 587 | - | ||
| 588 | 607 | self.set_docstring() | |
| 589 | 608 | ||
| 590 | - cw = self.cw() | ||
| 591 | - cs = self.current_string() | ||
| 592 | - if not cw: | ||
| 593 | - self.matches = [] | ||
| 594 | - self.matches_iter.update() | ||
| 595 | - if not (cw or cs): | ||
| 596 | - return bool(self.argspec) | ||
| 597 | - | ||
| 598 | - if cs and tab: | ||
| 599 | - self.matches = filename_matches(cs) | ||
| 600 | - self.matches_iter.update(cs, self.matches) | ||
| 601 | - return bool(self.matches) | ||
| 602 | - elif cs: | ||
| 603 | - # Do not provide suggestions inside strings, as one cannot tab | ||
| 604 | - # them so they would be really confusing. | ||
| 605 | - self.matches_iter.update() | ||
| 606 | - return False | ||
| 609 | + matches, replacer = autocomplete.find_matches( | ||
| 610 | + len(self.current_line()) - self.cpos, | ||
| 611 | + self.current_line(), | ||
| 612 | + self.interp.locals, | ||
| 613 | + self.argspec, | ||
| 614 | + self.config, | ||
| 615 | + self.magic_method_completions) | ||
| 607 | 616 | ||
| 608 | - # Check for import completion | ||
| 609 | - e = False | ||
| 610 | - matches = importcompletion.complete(len(self.current_line()) - self.cpos, self.current_line()) | ||
| 611 | - if matches is not None and not matches: | ||
| 612 | - self.matches = [] | ||
| 613 | - self.matches_iter.update() | ||
| 614 | - return False | ||
| 617 | + self.matches = matches | ||
| 618 | + self.matches_iter.update(len(self.current_line()) - self.cpos, | ||
| 619 | + self.current_line(), self.matches, replacer=replacer) | ||
| 615 | 620 | ||
| 616 | - if matches is None: | ||
| 617 | - # Nope, no import, continue with normal completion | ||
| 618 | - try: | ||
| 619 | - self.completer.complete(cw, 0) | ||
| 620 | - except Exception: | ||
| 621 | - # This sucks, but it's either that or list all the exceptions that could | ||
| 622 | - # possibly be raised here, so if anyone wants to do that, feel free to send me | ||
| 623 | - # a patch. XXX: Make sure you raise here if you're debugging the completion | ||
| 624 | - # stuff ! | ||
| 625 | - raise | ||
| 626 | - e = True | ||
| 627 | - else: | ||
| 628 | - matches = self.completer.matches | ||
| 629 | - matches.extend(self.magic_method_completions(cw)) | ||
| 630 | - | ||
| 631 | - if not e and self.argspec: | ||
| 632 | - matches.extend(name + '=' for name in self.argspec[1][0] | ||
| 633 | - if isinstance(name, basestring) and name.startswith(cw)) | ||
| 634 | - if py3: | ||
| 635 | - matches.extend(name + '=' for name in self.argspec[1][4] | ||
| 636 | - if name.startswith(cw)) | ||
| 637 | - | ||
| 638 | - # unless the first character is a _ filter out all attributes starting with a _ | ||
| 639 | - if not e and not cw.split('.')[-1].startswith('_'): | ||
| 640 | - matches = [match for match in matches | ||
| 641 | - if not match.split('.')[-1].startswith('_')] | ||
| 642 | - | ||
| 643 | - if e or not matches: | ||
| 644 | - self.matches = [] | ||
| 645 | - self.matches_iter.update() | ||
| 646 | - if not self.argspec: | ||
| 647 | - return False | ||
| 621 | + if matches is None or len(matches) == 0: | ||
| 622 | + return bool(self.matches or self.argspec) | ||
| 648 | 623 | else: | |
| 649 | - # remove duplicates | ||
| 650 | - self.matches = sorted(set(matches)) | ||
| 651 | - | ||
| 652 | - | ||
| 653 | - if len(self.matches) == 1 and not self.config.auto_display_list: | ||
| 654 | - self.list_win_visible = True | ||
| 655 | - self.tab() | ||
| 656 | - return False | ||
| 657 | - | ||
| 658 | - self.matches_iter.update(cw, self.matches) | ||
| 659 | - return True | ||
| 624 | + if len(self.matches) == 1 and not self.config.auto_display_list: | ||
| 625 | + self.list_win_visible = True | ||
| 626 | + self.tab() | ||
| 627 | + return False | ||
| 628 | + self.matches_iter.update(len(self.current_line()) - self.cpos, | ||
| 629 | + self.current_line(), self.matches, replacer) | ||
| 630 | + return True | ||
| 660 | 631 | ||
| 661 | 632 | def format_docstring(self, docstring, width, height): | |
| 662 | 633 | """Take a string and try to format it into a sane list of strings to be | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,18 @@ | |||
| 1 | + from bpython import autocomplete | ||
| 2 | + from functools import partial | ||
| 3 | + | ||
| 4 | + import unittest | ||
| 5 | + | ||
| 6 | + class TestSimpleComplete(unittest.TestCase): | ||
| 7 | + | ||
| 8 | + simple_config = type('', (), {})() | ||
| 9 | + simple_config.autocomplete_mode = autocomplete.SIMPLE | ||
| 10 | + complete = partial(autocomplete.complete, | ||
| 11 | + namespace={'zabcdef':1, 'zabcqwe':2, 'ze':3}, | ||
| 12 | + config=simple_config) | ||
| 13 | + | ||
| 14 | + def test_simple_completion(self): | ||
| 15 | + self.assertEqual(self.complete('zab'), ['zabcdef', 'zabcqwe']) | ||
| 16 | + self.assertEqual(self.complete('zabc'), ['zabcdef', 'zabcqwe']) | ||
| 17 | + self.assertEqual(self.complete('zabcd'), ['zabcdef']) | ||
| 18 | + | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -146,7 +146,7 @@ def test_simple(self): | |||
| 146 | 146 | class TestCurrentString(LineTestCase): | |
| 147 | 147 | def setUp(self): | |
| 148 | 148 | self.func = current_string | |
| 149 | - def test_simple(self): | ||
| 149 | + def test_closed(self): | ||
| 150 | 150 | self.assertAccess('"<as|df>"') | |
| 151 | 151 | self.assertAccess('"<asdf|>"') | |
| 152 | 152 | self.assertAccess('"<|asdf>"') | |
@@ -155,6 +155,15 @@ def test_simple(self): | |||
| 155 | 155 | self.assertAccess("'''<asdf|>'''") | |
| 156 | 156 | self.assertAccess('"""<asdf|>"""') | |
| 157 | 157 | self.assertAccess('asdf.afd("a") + "<asdf|>"') | |
| 158 | + def test_open(self): | ||
| 159 | + self.assertAccess('"<as|df>') | ||
| 160 | + self.assertAccess('"<asdf|>') | ||
| 161 | + self.assertAccess('"<|asdf>') | ||
| 162 | + self.assertAccess("'<asdf|>") | ||
| 163 | + self.assertAccess("'<|asdf>") | ||
| 164 | + self.assertAccess("'''<asdf|>") | ||
| 165 | + self.assertAccess('"""<asdf|>') | ||
| 166 | + self.assertAccess('asdf.afd("a") + "<asdf|>') | ||
| 158 | 167 | ||
| 159 | 168 | class TestCurrentObject(LineTestCase): | |
| 160 | 169 | def setUp(self): | |
| Back | FazBrowse Home | New Git URL |
0 commit comments