4646
4747#TODO other autocomplete modes (also fix in other bpython implementations)
4848
49+
4950from curtsies .configfile_keynames import keymap as key_dispatch
5051
5152logger = logging .getLogger (__name__ )
@@ -87,6 +88,7 @@ def __init__(self, coderunner, repl, configured_edit_keys=None):
8788
8889 def process_event (self , e ):
8990 assert self .has_focus
91+
9092 logger .debug ('fake input processing event %r' , e )
9193 if isinstance (e , events .PasteEvent ):
9294 for ee in e .events :
@@ -100,6 +102,9 @@ def process_event(self, e):
100102 self .current_line = ''
101103 self .cursor_offset = 0
102104 self .repl .run_code_and_maybe_finish ()
105+ elif e in ("<Esc+.>" ,):
106+ self .get_last_word ()
107+
103108 elif e in ["<ESC>" ]:
104109 pass
105110 elif e in ['<Ctrl-d>' ]:
@@ -469,6 +474,8 @@ def process_key_event(self, e):
469474 self .down_one_line ()
470475 elif e in ("<Ctrl-d>" ,):
471476 self .on_control_d ()
477+ elif e in ("<Esc+.>" ,):
478+ self .get_last_word ()
472479 elif e in ("<Esc+r>" ,):
473480 self .incremental_search (reverse = True )
474481 elif e in ("<Esc+s>" ,):
@@ -524,6 +531,21 @@ def process_key_event(self, e):
524531 else :
525532 self .add_normal_character (e )
526533
534+ def get_last_word (self ):
535+
536+ def last_word (line ):
537+ if not line :
538+ return ''
539+ return line .split ().pop ()
540+
541+ previous_word = last_word (self .rl_history .entry )
542+ word = last_word (self .rl_history .back ())
543+ line = self .current_line
544+ self ._set_current_line (line [:len (line )- len (previous_word )]+ word , reset_rl_history = False )
545+
546+ self ._set_cursor_offset (self .cursor_offset - len (previous_word )+ len (word ), reset_rl_history = False )
547+
548+
527549 def incremental_search (self , reverse = False , include_current = False ):
528550 if self .special_mode == None :
529551 self .rl_history .enter (self .current_line )
@@ -735,6 +757,19 @@ def update_completion(self, tab=False):
735757 self .current_match = None
736758 self .list_win_visible = BpythonRepl .complete (self , tab )
737759
760+ def predicted_indent (self , line ):
761+ #TODO get rid of this! It's repeated code! Combine with Repl.
762+ logger .debug ('line is %r' , line )
763+ indent = len (re .match (r'[ ]*' , line ).group ())
764+ if line .endswith (':' ):
765+ indent = max (0 , indent + self .config .tab_length )
766+ elif line and line .count (' ' ) == len (line ):
767+ indent = max (0 , indent - self .config .tab_length )
768+ elif line and ':' not in line and line .strip ().startswith (('return' , 'pass' , 'raise' , 'yield' )):
769+ indent = max (0 , indent - self .config .tab_length )
770+ logger .debug ('indent we found was %s' , indent )
771+ return indent
772+
738773 def push (self , line , insert_into_history = True ):
739774 """Push a line of code onto the buffer, start running the buffer
740775
@@ -743,14 +778,7 @@ def push(self, line, insert_into_history=True):
743778 if self .paste_mode :
744779 self .saved_indent = 0
745780 else :
746- indent = len (re .match (r'[ ]*' , line ).group ())
747- if line .endswith (':' ):
748- indent = max (0 , indent + self .config .tab_length )
749- elif line and line .count (' ' ) == len (line ):
750- indent = max (0 , indent - self .config .tab_length )
751- elif line and ':' not in line and line .strip ().startswith (('return' , 'pass' , 'raise' , 'yield' )):
752- indent = max (0 , indent - self .config .tab_length )
753- self .saved_indent = indent
781+ self .saved_indent = self .predicted_indent (line )
754782
755783 #current line not added to display buffer if quitting #TODO I don't understand this comment
756784 if self .config .syntax :
@@ -808,7 +836,8 @@ def run_code_and_maybe_finish(self, for_code=None):
808836 if err :
809837 indent = 0
810838
811- #TODO This should be printed ABOVE the error that just happened instead
839+
840+ #TODO This should be printed ABOVE the error that just happened instead
812841 # or maybe just thrown away and not shown
813842 if self .current_stdouterr_line :
814843 self .display_lines .extend (paint .display_linize (self .current_stdouterr_line , self .width ))
@@ -1193,6 +1222,20 @@ def reprint_line(self, lineno, tokens):
11931222 logger .debug ("calling reprint line with %r %r" , lineno , tokens )
11941223 if self .config .syntax :
11951224 self .display_buffer [lineno ] = bpythonparse (format (tokens , self .formatter ))
1225+
1226+ def take_back_buffer_line (self ):
1227+ self .display_buffer .pop ()
1228+ self .buffer .pop ()
1229+
1230+ if not self .buffer :
1231+ self .current_line = ''
1232+ self .cursor_offset = 0
1233+ else :
1234+ line = self .buffer [- 1 ]
1235+ indent = self .predicted_indent (line )
1236+ self .current_line = indent * ' '
1237+ self .cursor_offset = len (self .current_line )
1238+
11961239 def reevaluate (self , insert_into_history = False ):
11971240 """bpython.Repl.undo calls this"""
11981241 if self .watcher : self .watcher .reset ()
@@ -1257,14 +1300,17 @@ def pager(self, text):
12571300 self .focus_on_subprocess (command + [tmp .name ])
12581301
12591302 def show_source (self ):
1260- source = self .get_source_of_current_name ()
1261- if source is None :
1262- self .status_bar .message (_ ('Cannot show source.' ))
1263- else :
1303+ try :
1304+ source = self .get_source_of_current_name ()
12641305 if self .config .highlight_show_source :
1265- source = format (PythonLexer ().get_tokens (source ), TerminalFormatter ())
1306+ source = format (PythonLexer ().get_tokens (source ),
1307+ TerminalFormatter ())
12661308 self .pager (source )
1267-
1309+ except (ValueError , AttributeError , IOError , TypeError ), e :
1310+ self .status_bar .message (_ (e ))
1311+ except (NameError ), e :
1312+ self .status_bar .message (_ ('Cannot get source: %s' % e ))
1313+
12681314 def help_text (self ):
12691315 return (self .version_help_text () + '\n ' + self .key_help_text ()).encode ('utf8' )
12701316
0 commit comments