|
33 | 33 | raise SystemExit(1)
|
34 | 34 |
|
35 | 35 | from code import InteractiveInterpreter
|
| 36 | +import itertools |
36 | 37 | import linecache
|
37 | 38 | import os
|
38 | 39 | import os.path
|
@@ -865,6 +866,13 @@ class PyShell(OutputWindow):
|
865 | 866 | rmenu_specs = OutputWindow.rmenu_specs + [
|
866 | 867 | ("Squeeze", "<<squeeze-current-text>>"),
|
867 | 868 | ]
|
| 869 | + _idx = 1 + len(list(itertools.takewhile( |
| 870 | + lambda rmenu_item: rmenu_item[0] != "Copy", rmenu_specs) |
| 871 | + )) |
| 872 | + rmenu_specs.insert(_idx, ("Copy with prompts", |
| 873 | + "<<copy-with-prompts>>", |
| 874 | + "rmenu_check_copy")) |
| 875 | + del _idx |
868 | 876 |
|
869 | 877 | allow_line_numbers = False
|
870 | 878 | user_input_insert_tags = "stdin"
|
@@ -906,6 +914,7 @@ def __init__(self, flist=None):
|
906 | 914 | text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
|
907 | 915 | text.bind("<<toggle-debugger>>", self.toggle_debugger)
|
908 | 916 | text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
|
| 917 | + text.bind("<<copy-with-prompts>>", self.copy_with_prompts_callback) |
909 | 918 | if use_subprocess:
|
910 | 919 | text.bind("<<view-restart>>", self.view_restart_mark)
|
911 | 920 | text.bind("<<restart-shell>>", self.restart_shell)
|
@@ -979,6 +988,42 @@ def replace_event(self, event):
|
979 | 988 | def get_standard_extension_names(self):
|
980 | 989 | return idleConf.GetExtensions(shell_only=True)
|
981 | 990 |
|
| 991 | + def copy_with_prompts_callback(self, event=None): |
| 992 | + """Copy selected lines to the clipboard, with prompts. |
| 993 | +
|
| 994 | + This makes the copied text useful for doc-tests and interactive |
| 995 | + shell code examples. |
| 996 | +
|
| 997 | + This always copies entire lines, even if only part of the first |
| 998 | + and/or last lines is selected. |
| 999 | + """ |
| 1000 | + text = self.text |
| 1001 | + |
| 1002 | + selection_indexes = ( |
| 1003 | + self.text.index("sel.first linestart"), |
| 1004 | + self.text.index("sel.last +1line linestart"), |
| 1005 | + ) |
| 1006 | + if selection_indexes[0] is None: |
| 1007 | + # There is no selection, so do nothing. |
| 1008 | + return |
| 1009 | + |
| 1010 | + selected_text = self.text.get(*selection_indexes) |
| 1011 | + selection_lineno_range = range( |
| 1012 | + int(float(selection_indexes[0])), |
| 1013 | + int(float(selection_indexes[1])) |
| 1014 | + ) |
| 1015 | + prompts = [ |
| 1016 | + self.shell_sidebar.line_prompts.get(lineno) |
| 1017 | + for lineno in selection_lineno_range |
| 1018 | + ] |
| 1019 | + selected_text_with_prompts = "\n".join( |
| 1020 | + line if prompt is None else f"{prompt} {line}" |
| 1021 | + for prompt, line in zip(prompts, selected_text.splitlines()) |
| 1022 | + ) + "\n" |
| 1023 | + |
| 1024 | + text.clipboard_clear() |
| 1025 | + text.clipboard_append(selected_text_with_prompts) |
| 1026 | + |
982 | 1027 | reading = False
|
983 | 1028 | executing = False
|
984 | 1029 | canceled = False
|
|
0 commit comments