| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1256,16 +1256,22 @@ def current_output_line(self, value): | |||
| 1256 | 1256 | self.current_stdouterr_line = '' | |
| 1257 | 1257 | self.stdin.current_line = '\n' | |
| 1258 | 1258 | ||
| 1259 | - def paint(self, about_to_exit=False, user_quit=False): | ||
| 1259 | + def paint(self, about_to_exit=False, user_quit=False, | ||
| 1260 | + try_preserve_history_height=30, | ||
| 1261 | + min_infobox_height=5): | ||
| 1260 | 1262 | """Returns an array of min_height or more rows and width columns, plus | |
| 1261 | 1263 | cursor position | |
| 1262 | 1264 | ||
| 1263 | 1265 | Paints the entire screen - ideally the terminal display layer will take | |
| 1264 | 1266 | a diff and only write to the screen in portions that have changed, but | |
| 1265 | 1267 | the idea is that we don't need to worry about that here, instead every | |
| 1266 | 1268 | frame is completely redrawn because less state is cool! | |
| 1269 | + | ||
| 1270 | + try_preserve_history_height is the the number of rows of content that | ||
| 1271 | + must be visible before the suggestion box scrolls the terminal in order | ||
| 1272 | + to display more than min_infobox_height rows of suggestions, docs etc. | ||
| 1267 | 1273 | """ | |
| 1268 | - # The hairiest function in the curtsies - a cleanup would be great. | ||
| 1274 | + # The hairiest function in the curtsies | ||
| 1269 | 1275 | if about_to_exit: | |
| 1270 | 1276 | # exception to not changing state! | |
| 1271 | 1277 | self.clean_up_current_line_for_exit() | |
@@ -1415,10 +1421,18 @@ def move_screen_up(current_line_start_row): | |||
| 1415 | 1421 | if self.config.curtsies_list_above: | |
| 1416 | 1422 | info_max_rows = max(visible_space_above, visible_space_below) | |
| 1417 | 1423 | else: | |
| 1424 | + # Logic for determining size of completion box | ||
| 1418 | 1425 | # smallest allowed over-full completion box | |
| 1419 | - minimum_possible_height = 20 | ||
| 1426 | + preferred_height = max( | ||
| 1427 | + # always make infobox at least this height | ||
| 1428 | + min_infobox_height, | ||
| 1429 | + | ||
| 1430 | + # use this value if there's so much space that we can | ||
| 1431 | + # preserve this try_preserve_history_height rows history | ||
| 1432 | + min_height - try_preserve_history_height) | ||
| 1433 | + | ||
| 1420 | 1434 | info_max_rows = min(max(visible_space_below, | |
| 1421 | - minimum_possible_height), | ||
| 1435 | + preferred_height), | ||
| 1422 | 1436 | min_height - current_line_height - 1) | |
| 1423 | 1437 | infobox = paint.paint_infobox( | |
| 1424 | 1438 | info_max_rows, | |
@@ -1506,6 +1520,7 @@ def _set_current_line(self, line, update_completion=True, | |||
| 1506 | 1520 | if clear_special_mode: | |
| 1507 | 1521 | self.special_mode = None | |
| 1508 | 1522 | self.unhighlight_paren() | |
| 1523 | + | ||
| 1509 | 1524 | current_line = property(_get_current_line, _set_current_line, None, | |
| 1510 | 1525 | "The current line") | |
| 1511 | 1526 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,7 +1,9 @@ | |||
| 1 | 1 | # coding: utf8 | |
| 2 | 2 | from __future__ import unicode_literals | |
| 3 | - import sys | ||
| 3 | + import itertools | ||
| 4 | + import string | ||
| 4 | 5 | import os | |
| 6 | + import sys | ||
| 5 | 7 | from contextlib import contextmanager | |
| 6 | 8 | ||
| 7 | 9 | from curtsies.formatstringarray import FormatStringTest, fsarray | |
@@ -21,6 +23,7 @@ | |||
| 21 | 23 | def setup_config(): | |
| 22 | 24 | config_struct = config.Struct() | |
| 23 | 25 | config.loadini(config_struct, os.devnull) | |
| 26 | + config_struct.cli_suggestion_width = 1 | ||
| 24 | 27 | return config_struct | |
| 25 | 28 | ||
| 26 | 29 | ||
@@ -48,13 +51,18 @@ def _request_refresh(inner_self): | |||
| 48 | 51 | self.repl.rl_history = History() | |
| 49 | 52 | self.repl.height, self.repl.width = (5, 10) | |
| 50 | 53 | ||
| 54 | + @property | ||
| 55 | + def locals(self): | ||
| 56 | + return self.repl.coderunner.interp.locals | ||
| 57 | + | ||
| 51 | 58 | def assert_paint(self, screen, cursor_row_col): | |
| 52 | 59 | array, cursor_pos = self.repl.paint() | |
| 53 | 60 | self.assertFSArraysEqual(array, screen) | |
| 54 | 61 | self.assertEqual(cursor_pos, cursor_row_col) | |
| 55 | 62 | ||
| 56 | - def assert_paint_ignoring_formatting(self, screen, cursor_row_col=None): | ||
| 57 | - array, cursor_pos = self.repl.paint() | ||
| 63 | + def assert_paint_ignoring_formatting(self, screen, cursor_row_col=None, | ||
| 64 | + **paint_kwargs): | ||
| 65 | + array, cursor_pos = self.repl.paint(**paint_kwargs) | ||
| 58 | 66 | self.assertFSArraysEqualIgnoringFormatting(array, screen) | |
| 59 | 67 | if cursor_row_col is not None: | |
| 60 | 68 | self.assertEqual(cursor_pos, cursor_row_col) | |
@@ -96,15 +104,15 @@ def test_completion(self): | |||
| 96 | 104 | self.cursor_offset = 2 | |
| 97 | 105 | if config.supports_box_chars(): | |
| 98 | 106 | screen = ['>>> an', | |
| 99 | - '┌───────────────────────┐', | ||
| 100 | - '│ and any( │', | ||
| 101 | - '└───────────────────────┘', | ||
| 107 | + '┌──────────────────────────────┐', | ||
| 108 | + '│ and any( │', | ||
| 109 | + '└──────────────────────────────┘', | ||
| 102 | 110 | 'Welcome to bpython! Press <F1> f'] | |
| 103 | 111 | else: | |
| 104 | 112 | screen = ['>>> an', | |
| 105 | - '+-----------------------+', | ||
| 106 | - '| and any( |', | ||
| 107 | - '+-----------------------+', | ||
| 113 | + '+------------------------------+', | ||
| 114 | + '| and any( |', | ||
| 115 | + '+------------------------------+', | ||
| 108 | 116 | 'Welcome to bpython! Press <F1> f'] | |
| 109 | 117 | self.assert_paint_ignoring_formatting(screen, (0, 4)) | |
| 110 | 118 | ||
@@ -155,7 +163,7 @@ def output_to_repl(repl): | |||
| 155 | 163 | sys.stdout, sys.stderr = old_out, old_err | |
| 156 | 164 | ||
| 157 | 165 | ||
| 158 | - class TestCurtsiesRewindRedraw(CurtsiesPaintingTest): | ||
| 166 | + class HigherLevelCurtsiesPaintingTest(CurtsiesPaintingTest): | ||
| 159 | 167 | def refresh(self): | |
| 160 | 168 | self.refresh_requests.append(RefreshRequestEvent()) | |
| 161 | 169 | ||
@@ -194,6 +202,12 @@ def _request_refresh(inner_self): | |||
| 194 | 202 | self.repl.rl_history = History() | |
| 195 | 203 | self.repl.height, self.repl.width = (5, 32) | |
| 196 | 204 | ||
| 205 | + def send_key(self, key): | ||
| 206 | + self.repl.process_event('<SPACE>' if key == ' ' else key) | ||
| 207 | + self.repl.paint() # has some side effects we need to be wary of | ||
| 208 | + | ||
| 209 | + | ||
| 210 | + class TestCurtsiesRewindRedraw(HigherLevelCurtsiesPaintingTest): | ||
| 197 | 211 | def test_rewind(self): | |
| 198 | 212 | self.repl.current_line = '1 + 1' | |
| 199 | 213 | self.enter() | |
@@ -564,10 +578,6 @@ def test_unhighlight_paren_bugs(self): | |||
| 564 | 578 | green("... ") + yellow(')') + bold(cyan(" "))]) | |
| 565 | 579 | self.assert_paint(screen, (1, 6)) | |
| 566 | 580 | ||
| 567 | - def send_key(self, key): | ||
| 568 | - self.repl.process_event('<SPACE>' if key == ' ' else key) | ||
| 569 | - self.repl.paint() # has some side effects we need to be wary of | ||
| 570 | - | ||
| 571 | 581 | def test_472(self): | |
| 572 | 582 | [self.send_key(c) for c in "(1, 2, 3)"] | |
| 573 | 583 | with output_to_repl(self.repl): | |
@@ -586,3 +596,152 @@ def test_472(self): | |||
| 586 | 596 | '(1, 4, 3)', | |
| 587 | 597 | '>>> '] | |
| 588 | 598 | self.assert_paint_ignoring_formatting(screen, (4, 4)) | |
| 599 | + | ||
| 600 | + | ||
| 601 | + def completion_target(num_names, chars_in_first_name=1): | ||
| 602 | + class Class(object): | ||
| 603 | + pass | ||
| 604 | + | ||
| 605 | + if chars_in_first_name < 1: | ||
| 606 | + raise ValueError('need at least one char in each name') | ||
| 607 | + elif chars_in_first_name == 1 and num_names > len(string.ascii_letters): | ||
| 608 | + raise ValueError('need more chars to make so many names') | ||
| 609 | + | ||
| 610 | + names = gen_names() | ||
| 611 | + if num_names > 0: | ||
| 612 | + setattr(Class, 'a' * chars_in_first_name, 1) | ||
| 613 | + next(names) # use the above instead of first name | ||
| 614 | + for _, name in zip(range(num_names - 1), names): | ||
| 615 | + setattr(Class, name, 0) | ||
| 616 | + | ||
| 617 | + return Class() | ||
| 618 | + | ||
| 619 | + | ||
| 620 | + def gen_names(): | ||
| 621 | + for letters in itertools.chain( | ||
| 622 | + itertools.combinations_with_replacement(string.ascii_letters, 1), | ||
| 623 | + itertools.combinations_with_replacement(string.ascii_letters, 2)): | ||
| 624 | + yield ''.join(letters) | ||
| 625 | + | ||
| 626 | + | ||
| 627 | + class TestCompletionHelpers(TestCase): | ||
| 628 | + def test_gen_names(self): | ||
| 629 | + self.assertEqual(list(zip([1, 2, 3], gen_names())), | ||
| 630 | + [(1, 'a'), (2, 'b'), (3, 'c')]) | ||
| 631 | + | ||
| 632 | + def test_completion_target(self): | ||
| 633 | + target = completion_target(14) | ||
| 634 | + self.assertEqual(len([x for x in dir(target) | ||
| 635 | + if not x.startswith('_')]), | ||
| 636 | + 14) | ||
| 637 | + | ||
| 638 | + | ||
| 639 | + class TestCurtsiesInfoboxPaint(HigherLevelCurtsiesPaintingTest): | ||
| 640 | + def test_simple(self): | ||
| 641 | + self.repl.width, self.repl.height = (20, 30) | ||
| 642 | + self.locals['abc'] = completion_target(3, 50) | ||
| 643 | + self.repl.current_line = 'abc' | ||
| 644 | + self.repl.cursor_offset = 3 | ||
| 645 | + self.repl.process_event('.') | ||
| 646 | + screen = ['>>> abc.', | ||
| 647 | + '+------------------+', | ||
| 648 | + '| aaaaaaaaaaaaaaaa |', | ||
| 649 | + '| b |', | ||
| 650 | + '| c |', | ||
| 651 | + '+------------------+'] | ||
| 652 | + self.assert_paint_ignoring_formatting(screen, (0, 8)) | ||
| 653 | + | ||
| 654 | + def test_fill_screen(self): | ||
| 655 | + self.repl.width, self.repl.height = (20, 15) | ||
| 656 | + self.locals['abc'] = completion_target(20, 100) | ||
| 657 | + self.repl.current_line = 'abc' | ||
| 658 | + self.repl.cursor_offset = 3 | ||
| 659 | + self.repl.process_event('.') | ||
| 660 | + screen = ['>>> abc.', | ||
| 661 | + '+------------------+', | ||
| 662 | + '| aaaaaaaaaaaaaaaa |', | ||
| 663 | + '| b |', | ||
| 664 | + '| c |', | ||
| 665 | + '| d |', | ||
| 666 | + '| e |', | ||
| 667 | + '| f |', | ||
| 668 | + '| g |', | ||
| 669 | + '| h |', | ||
| 670 | + '| i |', | ||
| 671 | + '| j |', | ||
| 672 | + '| k |', | ||
| 673 | + '| l |', | ||
| 674 | + '+------------------+'] | ||
| 675 | + self.assert_paint_ignoring_formatting(screen, (0, 8)) | ||
| 676 | + | ||
| 677 | + def test_lower_on_screen(self): | ||
| 678 | + self.repl.get_top_usable_line = lambda: 10 # halfway down terminal | ||
| 679 | + self.repl.width, self.repl.height = (20, 15) | ||
| 680 | + self.locals['abc'] = completion_target(20, 100) | ||
| 681 | + self.repl.current_line = 'abc' | ||
| 682 | + self.repl.cursor_offset = 3 | ||
| 683 | + self.repl.process_event('.') | ||
| 684 | + screen = ['>>> abc.', | ||
| 685 | + '+------------------+', | ||
| 686 | + '| aaaaaaaaaaaaaaaa |', | ||
| 687 | + '| b |', | ||
| 688 | + '| c |', | ||
| 689 | + '| d |', | ||
| 690 | + '| e |', | ||
| 691 | + '| f |', | ||
| 692 | + '| g |', | ||
| 693 | + '| h |', | ||
| 694 | + '| i |', | ||
| 695 | + '| j |', | ||
| 696 | + '| k |', | ||
| 697 | + '| l |', | ||
| 698 | + '+------------------+'] | ||
| 699 | + # behavior before issue #466 | ||
| 700 | + self.assert_paint_ignoring_formatting( | ||
| 701 | + screen, try_preserve_history_height=0) | ||
| 702 | + self.assert_paint_ignoring_formatting( | ||
| 703 | + screen, min_infobox_height=100) | ||
| 704 | + # behavior after issue #466 | ||
| 705 | + screen = ['>>> abc.', | ||
| 706 | + '+------------------+', | ||
| 707 | + '| aaaaaaaaaaaaaaaa |', | ||
| 708 | + '| b |', | ||
| 709 | + '| c |', | ||
| 710 | + '+------------------+'] | ||
| 711 | + self.assert_paint_ignoring_formatting(screen) | ||
| 712 | + | ||
| 713 | + def test_at_bottom_of_screen(self): | ||
| 714 | + self.repl.get_top_usable_line = lambda: 17 # two lines from bottom | ||
| 715 | + self.repl.width, self.repl.height = (20, 15) | ||
| 716 | + self.locals['abc'] = completion_target(20, 100) | ||
| 717 | + self.repl.current_line = 'abc' | ||
| 718 | + self.repl.cursor_offset = 3 | ||
| 719 | + self.repl.process_event('.') | ||
| 720 | + screen = ['>>> abc.', | ||
| 721 | + '+------------------+', | ||
| 722 | + '| aaaaaaaaaaaaaaaa |', | ||
| 723 | + '| b |', | ||
| 724 | + '| c |', | ||
| 725 | + '| d |', | ||
| 726 | + '| e |', | ||
| 727 | + '| f |', | ||
| 728 | + '| g |', | ||
| 729 | + '| h |', | ||
| 730 | + '| i |', | ||
| 731 | + '| j |', | ||
| 732 | + '| k |', | ||
| 733 | + '| l |', | ||
| 734 | + '+------------------+'] | ||
| 735 | + # behavior before issue #466 | ||
| 736 | + self.assert_paint_ignoring_formatting( | ||
| 737 | + screen, try_preserve_history_height=0) | ||
| 738 | + self.assert_paint_ignoring_formatting( | ||
| 739 | + screen, min_infobox_height=100) | ||
| 740 | + # behavior after issue #466 | ||
| 741 | + screen = ['>>> abc.', | ||
| 742 | + '+------------------+', | ||
| 743 | + '| aaaaaaaaaaaaaaaa |', | ||
| 744 | + '| b |', | ||
| 745 | + '| c |', | ||
| 746 | + '+------------------+'] | ||
| 747 | + self.assert_paint_ignoring_formatting(screen) | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments