5151
5252logger = logging .getLogger (__name__ )
5353
54- HELP_MESSAGE = """
54+ INCONSISTENT_HISTORY_MSG = u"#<---History inconsistent with output shown--->"
55+ CONTIGUITY_BROKEN_MSG = u"#<---History contiguity broken by rewind--->"
56+ HELP_MESSAGE = u"""
5557Thanks for using bpython!
5658
5759See http://bpython-interpreter.org/ for info, http://docs.bpython-interpreter.org/ for docs, and https://github.com/bpython/bpython for source.
@@ -270,12 +272,7 @@ def smarter_request_reload(desc):
270272self .get_term_hw = get_term_hw
271273self .get_cursor_vertical_diff = get_cursor_vertical_diff
272274
273- self .status_bar = StatusBar (
274- (_ (" <%s> Rewind <%s> Save <%s> Pastebin <%s> Editor" )
275- % (config .undo_key ,config .save_key ,config .pastebin_key ,config .external_editor_key )
276- if config .curtsies_fill_terminal else '' ),
277- refresh_request = self .request_refresh
278- )
275+ self .status_bar = StatusBar ('' ,refresh_request = self .request_refresh )
279276self .edit_keys = edit_keys .mapping_with_config (config ,key_dispatch )
280277logger .debug ("starting parent init" )
281278super (Repl ,self ).__init__ (interp ,config )
@@ -308,6 +305,8 @@ def smarter_request_reload(desc):
308305self .stdin = FakeStdin (self .coderunner ,self ,self .edit_keys )
309306
310307self .request_paint_to_clear_screen = False # next paint should clear screen
308+ self .inconsistent_history = False # offscreen command yields different result from history
309+ self .history_already_messed_up = False # history error message displayed
311310self .last_events = [None ]* 50 # some commands act differently based on the prev event
312311# this list doesn't include instances of event.Event,
313312# only keypress-type events (no refresh screen events etc.)
@@ -1011,41 +1010,72 @@ def paint(self, about_to_exit=False, user_quit=False):
10111010self .clean_up_current_line_for_exit ()# exception to not changing state!
10121011
10131012width ,min_height = self .width ,self .height
1014- show_status_bar = bool (self .status_bar .should_show_message )or ( self .config . curtsies_fill_terminal or self . status_bar .has_focus )
1013+ show_status_bar = bool (self .status_bar .should_show_message )or self .status_bar .has_focus
10151014if show_status_bar :
1016- min_height -= 1
1015+ min_height -= 1 # because we're going to tack the status bar on at the end,
1016+ # shoot for an array one less than the height of the screen
10171017
10181018current_line_start_row = len (self .lines_for_display )- max (0 ,self .scroll_offset )
1019+ #TODO how is the situation of self.scroll_offset < 0 possible?
10191020#current_line_start_row = len(self.lines_for_display) - self.scroll_offset
10201021if self .request_paint_to_clear_screen :# or show_status_bar and about_to_exit ?
10211022self .request_paint_to_clear_screen = False
1022- if self .config .curtsies_fill_terminal :#TODO clean up this logic - really necessary check?
1023- arr = FSArray (self .height - 1 + current_line_start_row ,width )
1024- else :
1025- arr = FSArray (self .height + current_line_start_row ,width )
1023+ arr = FSArray (min_height + current_line_start_row ,width )
10261024else :
10271025arr = FSArray (0 ,width )
10281026#TODO test case of current line filling up the whole screen (there aren't enough rows to show it)
10291027
1030- if current_line_start_row < 0 :#if current line trying to be drawn off the top of the screen
1031- logger .debug ('#<---History contiguity broken by rewind--->' )
1032- msg = "#<---History contiguity broken by rewind--->"
1033- arr [0 ,0 :min (len (msg ),width )]= [msg [:width ]]
1034-
1028+ def move_screen_up (current_line_start_row ):
10351029# move screen back up a screen minus a line
10361030while current_line_start_row < 0 :
1031+ logger .debug ('scroll_offset was %s, current_line_start_row was %s' ,self .scroll_offset ,current_line_start_row )
10371032self .scroll_offset = self .scroll_offset - self .height
10381033current_line_start_row = len (self .lines_for_display )- max (- 1 ,self .scroll_offset )
1034+ logger .debug ('scroll_offset changed to %s, current_line_start_row changed to %s' ,self .scroll_offset ,current_line_start_row )
1035+ return current_line_start_row
1036+
1037+ if self .inconsistent_history == True and not self .history_already_messed_up :
1038+ logger .debug (INCONSISTENT_HISTORY_MSG )
1039+ self .history_already_messed_up = True
1040+ msg = INCONSISTENT_HISTORY_MSG
1041+ arr [0 ,0 :min (len (msg ),width )]= [msg [:width ]]
1042+ current_line_start_row += 1 # for the message
1043+ self .scroll_offset -= 1 # to make up for the scroll we're going to receive
1044+ # after we render scrolls down a line
1045+
1046+ current_line_start_row = move_screen_up (current_line_start_row )
1047+ logger .debug ('current_line_start_row: %r' ,current_line_start_row )
1048+
1049+ history = paint .paint_history (max (0 ,current_line_start_row - 1 ),width ,self .lines_for_display )
1050+ arr [1 :history .height + 1 ,:history .width ]= history
1051+
1052+ if arr .height <= min_height :
1053+ arr [min_height ,0 ]= ' ' # force scroll down to hide broken history message
1054+
1055+ elif current_line_start_row < 0 :#if current line trying to be drawn off the top of the screen
1056+ logger .debug (CONTIGUITY_BROKEN_MSG )
1057+ msg = CONTIGUITY_BROKEN_MSG
1058+ arr [0 ,0 :min (len (msg ),width )]= [msg [:width ]]
1059+
1060+ current_line_start_row = move_screen_up (current_line_start_row )
10391061
10401062history = paint .paint_history (max (0 ,current_line_start_row - 1 ),width ,self .lines_for_display )
10411063arr [1 :history .height + 1 ,:history .width ]= history
10421064
10431065if arr .height <= min_height :
10441066arr [min_height ,0 ]= ' ' # force scroll down to hide broken history message
1067+
10451068else :
1069+ assert current_line_start_row >= 0
1070+ logger .debug ("no history issues. start %i" ,current_line_start_row )
10461071history = paint .paint_history (current_line_start_row ,width ,self .lines_for_display )
10471072arr [:history .height ,:history .width ]= history
10481073
1074+
1075+
1076+
1077+ self .inconsistent_history = False
1078+
10491079current_line = paint .paint_current_line (min_height ,width ,self .current_cursor_line )
10501080if user_quit :# quit() or exit() in interp
10511081current_line_start_row = current_line_start_row - current_line .height
@@ -1072,7 +1102,7 @@ def paint(self, about_to_exit=False, user_quit=False):
10721102assert cursor_column >= 0 , (cursor_column ,len (self .current_cursor_line ),len (self .current_line ),self .cursor_offset )
10731103cursor_row += current_line_start_row
10741104
1075- if self .list_win_visible :
1105+ if self .list_win_visible and not self . coderunner . running :
10761106logger .debug ('infobox display code running' )
10771107visible_space_above = history .height
10781108visible_space_below = min_height - current_line_end_row - 1
@@ -1095,23 +1125,11 @@ def paint(self, about_to_exit=False, user_quit=False):
10951125
10961126logger .debug ('about to exit: %r' ,about_to_exit )
10971127if show_status_bar :
1098- if self .config .curtsies_fill_terminal :
1099- if about_to_exit :
1100- arr [max (arr .height ,min_height ), :]= FSArray (1 ,width )
1101- else :
1102- arr [max (arr .height ,min_height ), :]= paint .paint_statusbar (1 ,width ,self .status_bar .current_line ,self .config )
1103-
1104- if self .presentation_mode :
1105- rows = arr .height
1106- columns = arr .width
1107- last_key_box = paint .paint_last_events (rows ,columns , [events .pp_event (x )for x in self .last_events if x ])
1108- arr [arr .height - last_key_box .height :arr .height ,arr .width - last_key_box .width :arr .width ]= last_key_box
1128+ statusbar_row = min_height if arr .height == min_height else arr .height
1129+ if about_to_exit :
1130+ arr [statusbar_row , :]= FSArray (1 ,width )
11091131else :
1110- statusbar_row = min_height + 1 if arr .height == min_height else arr .height
1111- if about_to_exit :
1112- arr [statusbar_row , :]= FSArray (1 ,width )
1113- else :
1114- arr [statusbar_row , :]= paint .paint_statusbar (1 ,width ,self .status_bar .current_line ,self .config )
1132+ arr [statusbar_row , :]= paint .paint_statusbar (1 ,width ,self .status_bar .current_line ,self .config )
11151133
11161134if self .config .color_scheme ['background' ]not in ('d' ,'D' ):
11171135for r in range (arr .height ):
@@ -1240,6 +1258,7 @@ def reevaluate(self, insert_into_history=False):
12401258"""bpython.Repl.undo calls this"""
12411259if self .watcher :self .watcher .reset ()
12421260old_logical_lines = self .history
1261+ old_display_lines = self .display_lines
12431262self .history = []
12441263self .display_lines = []
12451264
@@ -1263,6 +1282,16 @@ def reevaluate(self, insert_into_history=False):
12631282sys .stdin = self .stdin
12641283self .reevaluating = False
12651284
1285+ num_lines_onscreen = len (self .lines_for_display )- max (0 ,self .scroll_offset )
1286+ display_lines_offscreen = self .display_lines [:len (self .display_lines )- num_lines_onscreen ]
1287+ old_display_lines_offscreen = old_display_lines [:len (self .display_lines )- num_lines_onscreen ]
1288+ logger .debug ('old_display_lines_offscreen %s' ,'|' .join ([str (x )for x in old_display_lines_offscreen ]))
1289+ logger .debug (' display_lines_offscreen %s' ,'|' .join ([str (x )for x in display_lines_offscreen ]))
1290+ if old_display_lines_offscreen [:len (display_lines_offscreen )]!= display_lines_offscreen and not self .history_already_messed_up :
1291+ #self.scroll_offset = self.scroll_offset + (len(old_display_lines)-len(self.display_lines))
1292+ self .inconsistent_history = True
1293+ logger .debug ('after rewind, self.inconsistent_history is %r' ,self .inconsistent_history )
1294+
12661295self .cursor_offset = 0
12671296self .current_line = ''
12681297