Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-37530: simplify, optimize and clean up IDLE code context #14675

Merged
merged 7 commits into from
Jul 17, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix a couple of bugs and update tests
  • Loading branch information
taleinat committed Jul 9, 2019
commit 7f40200167f011c9d885d12c8d76bbb2642e7e4a
12 changes: 5 additions & 7 deletions Lib/idlelib/codecontext.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,8 @@ def reload(cls):
def __del__(self):
"Cancel scheduled events."
if self.t1 is not None:
try:
self.text.after_cancel(self.t1)
except tkinter.TclError:
pass
self.text.after_cancel(self.t1)
self.t1 = None

def toggle_code_context_event(self, event=None):
"""Toggle code context display.
Expand Down Expand Up @@ -204,7 +202,7 @@ def jumptoline(self, event=None):
newtop = 1
else:
# Line number clicked.
contextline = self.editwin.getlineno('insert')
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]
Expand All @@ -217,9 +215,9 @@ def timer_event(self):
self.update_code_context()
self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event)

def update_font(self, font):
def update_font(self):
if self.context is not None:
self.context['font'] = font
self.context['font'] = self.text['font']

def update_highlight_colors(self):
if self.context is not None:
Expand Down
2 changes: 2 additions & 0 deletions Lib/idlelib/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,8 @@ def ResetFont(self):
# Called from configdialog.py

self.text['font'] = idleConf.GetFont(self.root, 'main','EditorWindow')
if self.codecontext is not None:
terryjreedy marked this conversation as resolved.
Show resolved Hide resolved
self.codecontext.update_font()

def RemoveKeybindings(self):
"Remove the keybindings before they are changed."
Expand Down
144 changes: 86 additions & 58 deletions Lib/idlelib/idle_test/test_codecontext.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from idlelib import codecontext
import unittest
import unittest.mock
from test.support import requires
from tkinter import Tk, Frame, Text, TclError

Expand Down Expand Up @@ -42,6 +43,9 @@ def __init__(self, root, frame, text):
self.text = text
self.label = ''

def getlineno(self, index):
return int(float(self.text.index(index)))

def update_menu_label(self, **kwargs):
self.label = kwargs['label']

Expand Down Expand Up @@ -75,6 +79,18 @@ def setUp(self):
self.text.yview(0)
self.cc = codecontext.CodeContext(self.editor)

self.highlight_cfg = {"background": '#abcdef',
"foreground": '#123456'}
orig_idleConf_GetHighlight = codecontext.idleConf.GetHighlight
def mock_idleconf_GetHighlight(theme, element):
if element == 'context':
return self.highlight_cfg
return orig_idleConf_GetHighlight(theme, element)
patcher = unittest.mock.patch.object(
codecontext.idleConf, 'GetHighlight', mock_idleconf_GetHighlight)
patcher.start()
self.addCleanup(patcher.stop)

def tearDown(self):
if self.cc.context:
self.cc.context.destroy()
Expand All @@ -89,30 +105,24 @@ def test_init(self):

eq(cc.editwin, ed)
eq(cc.text, ed.text)
eq(cc.textfont, ed.text['font'])
eq(cc.text['font'], ed.text['font'])
self.assertIsNone(cc.context)
eq(cc.info, [(0, -1, '', False)])
eq(cc.topvisible, 1)
eq(self.root.tk.call('after', 'info', self.cc.t1)[1], 'timer')
eq(self.root.tk.call('after', 'info', self.cc.t2)[1], 'timer')
self.assertIsNone(self.cc.t1)

def test_del(self):
self.cc.__del__()
with self.assertRaises(TclError) as msg:
self.root.tk.call('after', 'info', self.cc.t1)
self.assertIn("doesn't exist", msg)
with self.assertRaises(TclError) as msg:
self.root.tk.call('after', 'info', self.cc.t2)
self.assertIn("doesn't exist", msg)
# For coverage on the except. Have to delete because the
# above Tcl error is caught by after_cancel.
del self.cc.t1, self.cc.t2

def test_del_with_timer(self):
timer = self.cc.t1 = self.text.after(10000, lambda: None)
self.cc.__del__()
with self.assertRaises(TclError) as cm:
self.root.tk.call('after', 'info', timer)
self.assertIn("doesn't exist", str(cm.exception))

def test_reload(self):
codecontext.CodeContext.reload()
self.assertEqual(self.cc.colors, {'background': 'lightgray',
'foreground': '#000000'})
self.assertEqual(self.cc.context_depth, 15)

def test_toggle_code_context_event(self):
Expand All @@ -127,16 +137,18 @@ def test_toggle_code_context_event(self):
# Toggle on.
eq(toggle(), 'break')
self.assertIsNotNone(cc.context)
eq(cc.context['font'], cc.textfont)
eq(cc.context['fg'], cc.colors['foreground'])
eq(cc.context['bg'], cc.colors['background'])
eq(cc.context['font'], self.text['font'])
eq(cc.context['fg'], self.highlight_cfg['foreground'])
eq(cc.context['bg'], self.highlight_cfg['background'])
eq(cc.context.get('1.0', 'end-1c'), '')
eq(cc.editwin.label, 'Hide Code Context')
eq(self.root.tk.call('after', 'info', self.cc.t1)[1], 'timer')

# Toggle off.
eq(toggle(), 'break')
self.assertIsNone(cc.context)
eq(cc.editwin.label, 'Show Code Context')
self.assertIsNone(self.cc.t1)

def test_get_context(self):
eq = self.assertEqual
Expand Down Expand Up @@ -227,7 +239,7 @@ def test_update_code_context(self):
(4, 4, ' def __init__(self, a, b):', 'def')])
eq(cc.topvisible, 5)
eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n'
' def __init__(self, a, b):')
' def __init__(self, a, b):')

# Scroll down to line 11. Last 'def' is removed.
cc.text.yview(11)
Expand All @@ -239,9 +251,9 @@ def test_update_code_context(self):
(10, 8, ' elif a < b:', 'elif')])
eq(cc.topvisible, 12)
eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n'
' def compare(self):\n'
' if a > b:\n'
' elif a < b:')
' def compare(self):\n'
' if a > b:\n'
' elif a < b:')

# No scroll. No update, even though context_depth changed.
cc.update_code_context()
Expand All @@ -253,9 +265,9 @@ def test_update_code_context(self):
(10, 8, ' elif a < b:', 'elif')])
eq(cc.topvisible, 12)
eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n'
' def compare(self):\n'
' if a > b:\n'
' elif a < b:')
' def compare(self):\n'
' if a > b:\n'
' elif a < b:')

# Scroll up.
cc.text.yview(5)
Expand All @@ -276,15 +288,15 @@ def test_jumptoline(self):
cc.toggle_code_context_event()

# Empty context.
cc.text.yview(f'{2}.0')
cc.text.yview('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.text.yview('12.0')
cc.update_code_context()
eq(cc.topvisible, 12)
cc.context.mark_set('insert', '3.0')
Expand All @@ -293,7 +305,7 @@ def test_jumptoline(self):

# More context lines than limit.
cc.context_depth = 2
cc.text.yview(f'{12}.0')
cc.text.yview('12.0')
cc.update_code_context()
eq(cc.topvisible, 12)
cc.context.mark_set('insert', '1.0')
Expand All @@ -313,56 +325,72 @@ def test_timer_event(self, mock_update):
self.cc.timer_event()
mock_update.assert_called()

def test_config_timer_event(self):
def test_font(self):
eq = self.assertEqual
cc = self.cc
save_font = cc.text['font']
save_colors = codecontext.CodeContext.colors
test_font = 'FakeFont'
test_colors = {'background': '#222222', 'foreground': '#ffff00'}
test_font = 'TkFixedFont'

# Ensure code context is not active.
if cc.context:
cc.toggle_code_context_event()

# Nothing updates on inactive code context.
cc.text['font'] = test_font
codecontext.CodeContext.colors = test_colors
cc.config_timer_event()
eq(cc.textfont, save_font)
eq(cc.contextcolors, save_colors)
# Nothing breaks with inactive code context.
cc.update_font()

# Activate code context, but no change to font or color.
# Activate code context, but no change to font.
cc.toggle_code_context_event()
cc.text['font'] = save_font
codecontext.CodeContext.colors = save_colors
cc.config_timer_event()
eq(cc.textfont, save_font)
eq(cc.contextcolors, save_colors)
eq(cc.context['font'], save_font)
eq(cc.context['background'], save_colors['background'])
eq(cc.context['foreground'], save_colors['foreground'])
# Call font update, but no change to font.
cc.update_font()
eq(cc.context['font'], save_font)
cc.toggle_code_context_event()

# Active code context, change font.
# Change font and activate code context.
cc.text['font'] = test_font
cc.config_timer_event()
eq(cc.textfont, test_font)
eq(cc.contextcolors, save_colors)
cc.toggle_code_context_event()
eq(cc.context['font'], test_font)

# Change font and call the font update.
cc.update_font()
eq(cc.context['font'], save_font)
cc.text['font'] = save_font

def test_highlight_colors(self):
eq = self.assertEqual
cc = self.cc
save_colors = dict(self.highlight_cfg)
test_colors = {'background': '#222222', 'foreground': '#ffff00'}

# Ensure code context is not active.
if cc.context:
cc.toggle_code_context_event()

# Nothing breaks with inactive code context.
cc.update_highlight_colors()

# Activate code context, but no change to colors.
cc.toggle_code_context_event()
eq(cc.context['background'], save_colors['background'])
eq(cc.context['foreground'], save_colors['foreground'])

# Active code context, change color.
cc.text['font'] = save_font
codecontext.CodeContext.colors = test_colors
cc.config_timer_event()
eq(cc.textfont, save_font)
eq(cc.contextcolors, test_colors)
eq(cc.context['font'], save_font)
# Call colors update, but no change to font.
cc.update_highlight_colors()
eq(cc.context['background'], save_colors['background'])
eq(cc.context['foreground'], save_colors['foreground'])
cc.toggle_code_context_event()

# Change colors and activate code context.
self.highlight_cfg = test_colors
cc.toggle_code_context_event()
eq(cc.context['background'], test_colors['background'])
eq(cc.context['foreground'], test_colors['foreground'])
codecontext.CodeContext.colors = save_colors
cc.config_timer_event()

# Change colors and call highlight colors update.
self.highlight_cfg = save_colors
cc.update_highlight_colors()
eq(cc.context['background'], save_colors['background'])
eq(cc.context['foreground'], save_colors['foreground'])


class HelperFunctionText(unittest.TestCase):
Expand Down