Index: Bindings.py =================================================================== --- Bindings.py (revision 58196) +++ Bindings.py (working copy) @@ -36,6 +36,7 @@ ('Cu_t', '<>'), ('_Copy', '<>'), ('_Paste', '<>'), + ('Paste Code', '<>'), ('Select _All', '<>'), None, ('_Find...', '<>'), Index: config-keys.def =================================================================== --- config-keys.def (revision 58196) +++ config-keys.def (working copy) @@ -11,6 +11,7 @@ copy= cut= paste= +paste-code= beginning-of-line= center-insert= close-all-windows= @@ -62,6 +63,7 @@ copy= cut= paste= +paste-code= beginning-of-line= center-insert= close-all-windows= @@ -113,6 +115,7 @@ copy= cut= paste= +paste-code= beginning-of-line= center-insert= close-all-windows= @@ -212,3 +215,4 @@ open-window-from-file = python-docs = + Index: configHandler.py =================================================================== --- configHandler.py (revision 58196) +++ configHandler.py (working copy) @@ -547,6 +547,7 @@ '<>': ['', ''], '<>': ['', ''], '<>': ['', ''], + '<>': ['Control-Shift-v', 'Control-Shift-V'], '<>': ['', ''], '<>': [''], '<>': [''], Index: EditorWindow.py =================================================================== --- EditorWindow.py (revision 58196) +++ EditorWindow.py (working copy) @@ -132,6 +132,7 @@ text.bind("<>", self.cut) text.bind("<>", self.copy) text.bind("<>", self.paste) + text.bind("<>", self.paste_code) text.bind("<>", self.center_insert_event) text.bind("<>", self.help_dialog) text.bind("<>", self.python_docs) @@ -416,6 +417,58 @@ self.text.event_generate("<>") return "break" + def paste_code(self, event): + original_data = self.text.clipboard_get() + pasted_data = self._process_paste_code(data) + self.text.clipboard_clear() + self.text.clipboard_append(pasted_data) + return_value = self.paste(event) + self.text.clipboard_clear() + self.text.clipboard_append(original_data) + return return_value + + def _process_paste_code(self, code): + "Override this method to change how paste_code processes pasted text" + extra_newline = not (code and code[-1] == '\n') + if extra_newline: + code += '\n' + lines = code.splitlines(True) + + py_parser = PyParse.Parser(self.indentwidth, self.tabwidth) + py_parser.set_str(code) + py_parser._study1() + + # The list of lines is changed in-place because some lines are skipped, + # since only lines found in py_parser.goodlines are processed. + to_be_deleted = [] + for line_num in py_parser.goodlines: + if line_num >= len(lines): + break + line = self._process_pasted_good_line(lines[line_num]) + if line is None: + # None means remove the line + to_be_deleted.append(line_num) + else: + lines[line_num] = line + + # Remove lines which need to be deleted. + # This is done from last to first to avoid changing line indices. + to_be_deleted.sort() + to_be_deleted.reverse() + for line_num in to_be_deleted: + del lines[line_num] + + new_code = ''.join(lines) + if extra_newline: + new_code = new_code[:-1] + return new_code + + def _process_pasted_good_line(self, line): + "Override this method to change how paste_code processes single lines" + if line[:4] in ['>>> ', '... ']: + line = line[4:] + return line + def select_all(self, event=None): self.text.tag_add("sel", "1.0", "end-1c") self.text.mark_set("insert", "1.0") Index: PyShell.py =================================================================== --- PyShell.py (revision 58196) +++ PyShell.py (working copy) @@ -1240,6 +1240,12 @@ if not use_subprocess: raise KeyboardInterrupt + def _process_pasted_good_line(self, line): + if not line.rstrip(): + # remove whitespace lines + return None + return OutputWindow._process_pasted_good_line(self, line) + class PseudoFile(object): def __init__(self, shell, tags, encoding=None):