11# coding: utf8
22from __future__ import unicode_literals
3- import sys
3+ import itertools
4+ import string
45import os
6+ import sys
57from contextlib import contextmanager
68
79from curtsies .formatstringarray import FormatStringTest , fsarray
2123def setup_config ():
2224 config_struct = config .Struct ()
2325 config .loadini (config_struct , os .devnull )
26+ config_struct .cli_suggestion_width = 1
2427 return config_struct
2528
2629
@@ -48,13 +51,18 @@ def _request_refresh(inner_self):
4851 self .repl .rl_history = History ()
4952 self .repl .height , self .repl .width = (5 , 10 )
5053
54+ @property
55+ def locals (self ):
56+ return self .repl .coderunner .interp .locals
57+
5158 def assert_paint (self , screen , cursor_row_col ):
5259 array , cursor_pos = self .repl .paint ()
5360 self .assertFSArraysEqual (array , screen )
5461 self .assertEqual (cursor_pos , cursor_row_col )
5562
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 )
5866 self .assertFSArraysEqualIgnoringFormatting (array , screen )
5967 if cursor_row_col is not None :
6068 self .assertEqual (cursor_pos , cursor_row_col )
@@ -96,15 +104,15 @@ def test_completion(self):
96104 self .cursor_offset = 2
97105 if config .supports_box_chars ():
98106 screen = ['>>> an' ,
99- '┌───────────────────────┐' ,
100- '│ and any( │' ,
101- '└───────────────────────┘' ,
107+ '┌────────────────────────────── ┐' ,
108+ '│ and any( │' ,
109+ '└────────────────────────────── ┘' ,
102110 'Welcome to bpython! Press <F1> f' ]
103111 else :
104112 screen = ['>>> an' ,
105- '+-----------------------+' ,
106- '| and any( |' ,
107- '+-----------------------+' ,
113+ '+------------------------------ +' ,
114+ '| and any( |' ,
115+ '+------------------------------ +' ,
108116 'Welcome to bpython! Press <F1> f' ]
109117 self .assert_paint_ignoring_formatting (screen , (0 , 4 ))
110118
@@ -155,7 +163,7 @@ def output_to_repl(repl):
155163 sys .stdout , sys .stderr = old_out , old_err
156164
157165
158- class TestCurtsiesRewindRedraw (CurtsiesPaintingTest ):
166+ class HigherLevelCurtsiesPaintingTest (CurtsiesPaintingTest ):
159167 def refresh (self ):
160168 self .refresh_requests .append (RefreshRequestEvent ())
161169
@@ -194,6 +202,12 @@ def _request_refresh(inner_self):
194202 self .repl .rl_history = History ()
195203 self .repl .height , self .repl .width = (5 , 32 )
196204
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 ):
197211 def test_rewind (self ):
198212 self .repl .current_line = '1 + 1'
199213 self .enter ()
@@ -564,10 +578,6 @@ def test_unhighlight_paren_bugs(self):
564578 green ("... " ) + yellow (')' ) + bold (cyan (" " ))])
565579 self .assert_paint (screen , (1 , 6 ))
566580
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-
571581 def test_472 (self ):
572582 [self .send_key (c ) for c in "(1, 2, 3)" ]
573583 with output_to_repl (self .repl ):
@@ -586,3 +596,128 @@ def test_472(self):
586596 '(1, 4, 3)' ,
587597 '>>> ' ]
588598 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+ self .assert_paint_ignoring_formatting (screen , (0 , 8 ))
700+
701+ def test_at_bottom_of_screen (self ):
702+ self .repl .get_top_usable_line = lambda : 17 # two lines from bottom
703+ self .repl .width , self .repl .height = (20 , 15 )
704+ self .locals ['abc' ] = completion_target (20 , 100 )
705+ self .repl .current_line = 'abc'
706+ self .repl .cursor_offset = 3
707+ self .repl .process_event ('.' )
708+ screen = ['>>> abc.' ,
709+ '+------------------+' ,
710+ '| aaaaaaaaaaaaaaaa |' ,
711+ '| b |' ,
712+ '| c |' ,
713+ '| d |' ,
714+ '| e |' ,
715+ '| f |' ,
716+ '| g |' ,
717+ '| h |' ,
718+ '| i |' ,
719+ '| j |' ,
720+ '| k |' ,
721+ '| l |' ,
722+ '+------------------+' ]
723+ self .assert_paint_ignoring_formatting (screen , (0 , 8 ))
0 commit comments