bpo-37903: IDLE: add shell sidebar mouse interactions (GH-25708) · python/cpython@b43cc31

@@ -33,6 +33,7 @@

3333

raise SystemExit(1)

34343535

from code import InteractiveInterpreter

36+

import itertools

3637

import linecache

3738

import os

3839

import os.path

@@ -865,6 +866,13 @@ class PyShell(OutputWindow):

865866

rmenu_specs = OutputWindow.rmenu_specs + [

866867

("Squeeze", "<<squeeze-current-text>>"),

867868

]

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

868876869877

allow_line_numbers = False

870878

user_input_insert_tags = "stdin"

@@ -906,6 +914,7 @@ def __init__(self, flist=None):

906914

text.bind("<<open-stack-viewer>>", self.open_stack_viewer)

907915

text.bind("<<toggle-debugger>>", self.toggle_debugger)

908916

text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)

917+

text.bind("<<copy-with-prompts>>", self.copy_with_prompts_callback)

909918

if use_subprocess:

910919

text.bind("<<view-restart>>", self.view_restart_mark)

911920

text.bind("<<restart-shell>>", self.restart_shell)

@@ -979,6 +988,42 @@ def replace_event(self, event):

979988

def get_standard_extension_names(self):

980989

return idleConf.GetExtensions(shell_only=True)

981990991+

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+9821027

reading = False

9831028

executing = False

9841029

canceled = False