diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 73d3ba61742..8b378bceba2 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -117,6 +117,7 @@ class CodeContext: height=1, width=1, # Don't request more than we get. padx=padx, border=border, relief=SUNKEN, state='disabled') + self.context.bind('', self.jumptoline) # Pack the context widget before and above the text_frame widget, # thus ensuring that it will appear directly above text_frame. self.context.pack(side=TOP, fill=X, expand=False, @@ -196,6 +197,20 @@ class CodeContext: self.context.insert('end', '\n'.join(context_strings[showfirst:])) self.context['state'] = 'disabled' + def jumptoline(self, event=None): + "Show clicked context line at top of editor." + lines = len(self.info) + if lines == 1: # No context lines are showing. + newtop = 1 + 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): "Event on editor text widget triggered every UPDATEINTERVAL ms." if self.context: diff --git a/Lib/idlelib/idle_test/test_codecontext.py b/Lib/idlelib/idle_test/test_codecontext.py index 07e10b6f7ec..2e59b8501c9 100644 --- a/Lib/idlelib/idle_test/test_codecontext.py +++ b/Lib/idlelib/idle_test/test_codecontext.py @@ -72,6 +72,7 @@ class CodeContextTest(unittest.TestCase): del cls.root def setUp(self): + self.text.yview(0) self.cc = codecontext.CodeContext(self.editor) def tearDown(self): @@ -264,6 +265,39 @@ class CodeContextTest(unittest.TestCase): # context_depth is 1. eq(cc.context.get('1.0', 'end-1c'), ' def __init__(self, a, b):') + def test_jumptoline(self): + eq = self.assertEqual + cc = self.cc + jump = cc.jumptoline + + if not cc.context: + cc.toggle_code_context_event() + + # Empty context. + cc.text.yview(f'{2}.0') + cc.update_code_context() + eq(cc.topvisible, 2) + cc.context.mark_set('insert', '1.5') + jump() + eq(cc.topvisible, 1) + + # 4 lines of context showing. + cc.text.yview(f'{12}.0') + cc.update_code_context() + eq(cc.topvisible, 12) + cc.context.mark_set('insert', '3.0') + jump() + eq(cc.topvisible, 8) + + # More context lines than limit. + cc.context_depth = 2 + cc.text.yview(f'{12}.0') + cc.update_code_context() + eq(cc.topvisible, 12) + cc.context.mark_set('insert', '1.0') + jump() + eq(cc.topvisible, 8) + @mock.patch.object(codecontext.CodeContext, 'update_code_context') def test_timer_event(self, mock_update): # Ensure code context is not active. diff --git a/Misc/NEWS.d/next/IDLE/2018-06-04-19-23-11.bpo-33768.I_2qpV.rst b/Misc/NEWS.d/next/IDLE/2018-06-04-19-23-11.bpo-33768.I_2qpV.rst new file mode 100644 index 00000000000..689aede15ac --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2018-06-04-19-23-11.bpo-33768.I_2qpV.rst @@ -0,0 +1 @@ +Clicking on a context line moves that line to the top of the editor window.