bpo-37903: IDLE: add shell sidebar mouse interactions (GH-25708)

Left click and drag to select lines.  With selection, right click for context menu with copy and copy-with-prompts.
Also add copy-with-prompts to the text-box context menu.

Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu>
This commit is contained in:
Tal Einat 2021-05-03 05:27:38 +03:00 committed by GitHub
parent 90d523910a
commit b43cc31a27
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 384 additions and 230 deletions

View file

@ -33,6 +33,7 @@ if TkVersion < 8.5:
raise SystemExit(1)
from code import InteractiveInterpreter
import itertools
import linecache
import os
import os.path
@ -865,6 +866,13 @@ class PyShell(OutputWindow):
rmenu_specs = OutputWindow.rmenu_specs + [
("Squeeze", "<<squeeze-current-text>>"),
]
_idx = 1 + len(list(itertools.takewhile(
lambda rmenu_item: rmenu_item[0] != "Copy", rmenu_specs)
))
rmenu_specs.insert(_idx, ("Copy with prompts",
"<<copy-with-prompts>>",
"rmenu_check_copy"))
del _idx
allow_line_numbers = False
user_input_insert_tags = "stdin"
@ -906,6 +914,7 @@ class PyShell(OutputWindow):
text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
text.bind("<<toggle-debugger>>", self.toggle_debugger)
text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
text.bind("<<copy-with-prompts>>", self.copy_with_prompts_callback)
if use_subprocess:
text.bind("<<view-restart>>", self.view_restart_mark)
text.bind("<<restart-shell>>", self.restart_shell)
@ -979,6 +988,42 @@ class PyShell(OutputWindow):
def get_standard_extension_names(self):
return idleConf.GetExtensions(shell_only=True)
def copy_with_prompts_callback(self, event=None):
"""Copy selected lines to the clipboard, with prompts.
This makes the copied text useful for doc-tests and interactive
shell code examples.
This always copies entire lines, even if only part of the first
and/or last lines is selected.
"""
text = self.text
selection_indexes = (
self.text.index("sel.first linestart"),
self.text.index("sel.last +1line linestart"),
)
if selection_indexes[0] is None:
# There is no selection, so do nothing.
return
selected_text = self.text.get(*selection_indexes)
selection_lineno_range = range(
int(float(selection_indexes[0])),
int(float(selection_indexes[1]))
)
prompts = [
self.shell_sidebar.line_prompts.get(lineno)
for lineno in selection_lineno_range
]
selected_text_with_prompts = "\n".join(
line if prompt is None else f"{prompt} {line}"
for prompt, line in zip(prompts, selected_text.splitlines())
) + "\n"
text.clipboard_clear()
text.clipboard_append(selected_text_with_prompts)
reading = False
executing = False
canceled = False