bpo-39781: Do not jump when select in IDLE codecontext (GH-18683)

Previously, the button-up part of selecting with a mouse was treated as a click
that meant 'jump' to this line, which modified the context and undid the selection
(cherry picked from commit c705fd1e89)

Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu>
This commit is contained in:
Miss Islington (bot) 2020-02-28 10:41:17 -08:00 committed by GitHub
parent 3bf9de2fb9
commit f4198aee4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 36 additions and 19 deletions

View File

@ -3,6 +3,8 @@ Released on 2019-12-16?
====================================== ======================================
bpo-39781: Selecting code context lines no longer causes a jump.
bpo-39663: Add tests for pyparse find_good_parse_start(). bpo-39663: Add tests for pyparse find_good_parse_start().
bpo-39600: Remove duplicate font names from configuration list. bpo-39600: Remove duplicate font names from configuration list.

View File

@ -7,7 +7,6 @@ the lines which contain the block opening keywords, e.g. 'if', for the
enclosing block. The number of hint lines is determined by the maxlines enclosing block. The number of hint lines is determined by the maxlines
variable in the codecontext section of config-extensions.def. Lines which do variable in the codecontext section of config-extensions.def. Lines which do
not open blocks are not shown in the context hints pane. not open blocks are not shown in the context hints pane.
""" """
import re import re
from sys import maxsize as INFINITY from sys import maxsize as INFINITY
@ -17,8 +16,8 @@ from tkinter.constants import NSEW, SUNKEN
from idlelib.config import idleConf from idlelib.config import idleConf
BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for", BLOCKOPENERS = {'class', 'def', 'if', 'elif', 'else', 'while', 'for',
"if", "try", "while", "with", "async"} 'try', 'except', 'finally', 'with', 'async'}
def get_spaces_firstword(codeline, c=re.compile(r"^(\s*)(\w*)")): def get_spaces_firstword(codeline, c=re.compile(r"^(\s*)(\w*)")):
@ -84,7 +83,7 @@ class CodeContext:
if self.t1 is not None: if self.t1 is not None:
try: try:
self.text.after_cancel(self.t1) self.text.after_cancel(self.t1)
except tkinter.TclError: except tkinter.TclError: # pragma: no cover
pass pass
self.t1 = None self.t1 = None
@ -112,7 +111,7 @@ class CodeContext:
padx += widget.tk.getint(info['padx']) padx += widget.tk.getint(info['padx'])
padx += widget.tk.getint(widget.cget('padx')) padx += widget.tk.getint(widget.cget('padx'))
border += widget.tk.getint(widget.cget('border')) border += widget.tk.getint(widget.cget('border'))
self.context = tkinter.Text( context = self.context = tkinter.Text(
self.editwin.text_frame, self.editwin.text_frame,
height=1, height=1,
width=1, # Don't request more than we get. width=1, # Don't request more than we get.
@ -120,11 +119,11 @@ class CodeContext:
padx=padx, border=border, relief=SUNKEN, state='disabled') padx=padx, border=border, relief=SUNKEN, state='disabled')
self.update_font() self.update_font()
self.update_highlight_colors() self.update_highlight_colors()
self.context.bind('<ButtonRelease-1>', self.jumptoline) context.bind('<ButtonRelease-1>', self.jumptoline)
# Get the current context and initiate the recurring update event. # Get the current context and initiate the recurring update event.
self.timer_event() self.timer_event()
# Grid the context widget above the text widget. # Grid the context widget above the text widget.
self.context.grid(row=0, column=1, sticky=NSEW) context.grid(row=0, column=1, sticky=NSEW)
line_number_colors = idleConf.GetHighlight(idleConf.CurrentTheme(), line_number_colors = idleConf.GetHighlight(idleConf.CurrentTheme(),
'linenumber') 'linenumber')
@ -215,18 +214,25 @@ class CodeContext:
self.context['state'] = 'disabled' self.context['state'] = 'disabled'
def jumptoline(self, event=None): def jumptoline(self, event=None):
"Show clicked context line at top of editor." """ Show clicked context line at top of editor.
lines = len(self.info)
if lines == 1: # No context lines are showing. If a selection was made, don't jump; allow copying.
newtop = 1 If no visible context, show the top line of the file.
else: """
# Line number clicked. try:
contextline = int(float(self.context.index('insert'))) self.context.index("sel.first")
# Lines not displayed due to maxlines. except tkinter.TclError:
offset = max(1, lines - self.context_depth) - 1 lines = len(self.info)
newtop = self.info[offset + contextline][0] if lines == 1: # No context lines are showing.
self.text.yview(f'{newtop}.0') newtop = 1
self.update_code_context() else:
# Line number clicked.
contextline = int(float(self.context.index('insert')))
# Lines not displayed due to maxlines.
offset = max(1, lines - self.context_depth) - 1
newtop = self.info[offset + contextline][0]
self.text.yview(f'{newtop}.0')
self.update_code_context()
def timer_event(self): def timer_event(self):
"Event on editor text widget triggered every UPDATEINTERVAL ms." "Event on editor text widget triggered every UPDATEINTERVAL ms."

View File

@ -332,6 +332,14 @@ class CodeContextTest(unittest.TestCase):
jump() jump()
eq(cc.topvisible, 8) eq(cc.topvisible, 8)
# Context selection stops jump.
cc.text.yview('5.0')
cc.update_code_context()
cc.context.tag_add('sel', '1.0', '2.0')
cc.context.mark_set('insert', '1.0')
jump() # Without selection, to line 2.
eq(cc.topvisible, 5)
@mock.patch.object(codecontext.CodeContext, 'update_code_context') @mock.patch.object(codecontext.CodeContext, 'update_code_context')
def test_timer_event(self, mock_update): def test_timer_event(self, mock_update):
# Ensure code context is not active. # Ensure code context is not active.

View File

@ -0,0 +1 @@
Selecting code context lines no longer causes a jump.