3434import sys
3535import tempfile
3636import textwrap
37+ import time
3738import traceback
3839import unicodedata
3940from itertools import takewhile
5354import bpython .autocomplete as autocomplete
5455
5556
57+ class RuntimeTimer (object ):
58+ def __init__ (self ):
59+ self .reset_timer ()
60+
61+ def __enter__ (self ):
62+ self .start = time .time ()
63+
64+ def __exit__ (self , ty , val , tb ):
65+ self .last_command = time .time () - self .start
66+ self .running_time += self .last_command
67+ return False
68+
69+ def reset_timer (self ):
70+ self .running_time = 0.0
71+ self .last_command = 0.0
72+
73+ def estimate (self ):
74+ return self .running_time - self .last_command
75+
5676class Interpreter (code .InteractiveInterpreter ):
5777
5878 def __init__ (self , locals = None , encoding = None ):
@@ -68,16 +88,28 @@ def __init__(self, locals=None, encoding=None):
6888 self .syntaxerror_callback = None
6989 # Unfortunately code.InteractiveInterpreter is a classic class, so no super()
7090 code .InteractiveInterpreter .__init__ (self , locals )
91+ self .timer = RuntimeTimer ()
92+
93+ def reset_running_time (self ):
94+ self .running_time = 0
95+
96+ if py3 :
7197
72- if not py3 :
98+ def runsource (self , source , filename = "<input>" , symbol = "single" ):
99+ with self .timer :
100+ return code .InteractiveInterpreter .runsource (self , source ,
101+ filename , symbol )
102+
103+ else :
73104
74105 def runsource (self , source , filename = '<input>' , symbol = 'single' ,
75106 encode = True ):
76- if encode :
77- source = '# coding: %s\n %s' % (self .encoding ,
78- source .encode (self .encoding ))
79- return code .InteractiveInterpreter .runsource (self , source ,
80- filename , symbol )
107+ with self .timer :
108+ if encode :
109+ source = '# coding: %s\n %s' % (self .encoding ,
110+ source .encode (self .encoding ))
111+ return code .InteractiveInterpreter .runsource (self , source ,
112+ filename , symbol )
81113
82114 def showsyntaxerror (self , filename = None ):
83115 """Override the regular handler, the code's copied and pasted from
@@ -791,14 +823,44 @@ def insert_into_history(self, s):
791823 except RuntimeError as e :
792824 self .interact .notify (str (e ))
793825
826+ def prompt_undo (self ):
827+ """Returns how many lines to undo, 0 means don't undo"""
828+ self .config .single_undo_time = 1
829+ if self .interp .timer .estimate () < self .config .single_undo_time :
830+ return 1
831+ est = self .interp .timer .estimate ()
832+ n = self .interact .file_prompt (
833+ _ ("Undo how many lines? (Undo will take up to ~%.1f seconds) [1]" )
834+ % (est ,))
835+ try :
836+ if n == '' :
837+ n = '1'
838+ n = int (n )
839+ except ValueError :
840+ self .interact .notify (_ ('Undo canceled' ), .1 )
841+ return 0
842+ else :
843+ if n == 0 :
844+ self .interact .notify (_ ('Undo canceled' ), .1 )
845+ return 0
846+ if n == 1 :
847+ self .interact .notify (_ ('Undoing 1 line... (est. %.1f seconds)' )
848+ % (est ,), .1 )
849+ else :
850+ self .interact .notify (_ ('Undoing %d lines... (est. %.1f seconds)' )
851+ % (n , est ), .1 )
852+ return n
853+
794854 def undo (self , n = 1 ):
795- """Go back in the undo history n steps and call reeavluate ()
855+ """Go back in the undo history n steps and call reevaluate ()
796856 Note that in the program this is called "Rewind" because I
797857 want it to be clear that this is by no means a true undo
798858 implementation, it is merely a convenience bonus."""
799859 if not self .history :
800860 return None
801861
862+ self .interp .timer .reset_timer ()
863+
802864 if len (self .history ) < n :
803865 n = len (self .history )
804866
0 commit comments