bpo-33642: IDLE: Use variable number of lines in CodeContext.
diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py
index 635f68c86e1f6d..b061f3b2fe8eb9 100644
--- a/Lib/idlelib/codecontext.py
+++ b/Lib/idlelib/codecontext.py
@@ -4,7 +4,7 @@
 determine which block you are in.  This extension implements a pane at the top
 of each IDLE edit window which provides block structure hints.  These hints are
 the lines which contain the block opening keywords, e.g. 'if', for the
-enclosing block.  The number of hint lines is determined by the numlines
+enclosing block.  The number of hint lines is determined by the maxlines
 variable in the codecontext section of config-extensions.def. Lines which do
 not open blocks are not shown in the context hints pane.
 
@@ -80,7 +80,7 @@ def __init__(self, editwin):
     def reload(cls):
         "Load class variables from config."
         cls.context_depth = idleConf.GetOption("extensions", "CodeContext",
-                                       "numlines", type="int", default=3)
+                                       "maxlines", type="int", default=15)
 ##        cls.bgcolor = idleConf.GetOption("extensions", "CodeContext",
 ##                                     "bgcolor", type="str", default="LightGray")
 ##        cls.fgcolor = idleConf.GetOption("extensions", "CodeContext",
@@ -116,7 +116,7 @@ def toggle_code_context_event(self, event=None):
                 padx += widget.tk.getint(widget.cget('padx'))
                 border += widget.tk.getint(widget.cget('border'))
             self.label = tkinter.Label(
-                    self.editwin.top, text="\n" * (self.context_depth - 1),
+                    self.editwin.top, text="",
                     anchor=W, justify=LEFT, font=self.textfont,
                     bg=self.bgcolor, fg=self.fgcolor,
                     width=1,  # Don't request more than we get.
@@ -191,11 +191,10 @@ def update_code_context(self):
                                                  stopindent)
         self.info.extend(lines)
         self.topvisible = new_topvisible
-        # Empty lines in context pane.
-        context_strings = [""] * max(0, self.context_depth - len(self.info))
-        # Followed by the context hint lines.
-        context_strings += [x[2] for x in self.info[-self.context_depth:]]
-        self.label["text"] = '\n'.join(context_strings)
+        # Last context_depth context lines.
+        context_strings = [x[2] for x in self.info[-self.context_depth:]]
+        showfirst = 0 if context_strings[0] else 1
+        self.label["text"] = '\n'.join(context_strings[showfirst:])
 
     def timer_event(self):
         "Event on editor text widget triggered every UPDATEINTERVAL ms."
diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def
index 8689fd965e8eae..7e23fb0a73d1d5 100644
--- a/Lib/idlelib/config-extensions.def
+++ b/Lib/idlelib/config-extensions.def
@@ -7,10 +7,7 @@
 popupwait= 2000
 
 [CodeContext]
-numlines= 3
-visible= False
-bgcolor= LightGray
-fgcolor= Black
+maxlines= 15
 
 [FormatParagraph]
 max-width= 72
diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py
index 36ac4a23a52dc0..28ad4586394137 100644
--- a/Lib/idlelib/configdialog.py
+++ b/Lib/idlelib/configdialog.py
@@ -1843,7 +1843,7 @@ def create_page_general(self):
         self.format_width = tracers.add(
                 StringVar(self), ('extensions', 'FormatParagraph', 'max-width'))
         self.context_lines = tracers.add(
-                StringVar(self), ('extensions', 'CodeContext', 'numlines'))
+                StringVar(self), ('extensions', 'CodeContext', 'maxlines'))
 
         # Create widgets:
         # Section frames.
@@ -1910,7 +1910,7 @@ def create_page_general(self):
                 frame_format, textvariable=self.format_width, width=4)
 
         frame_context = Frame(frame_editor, borderwidth=0)
-        context_title = Label(frame_context, text='Context Lines :')
+        context_title = Label(frame_context, text='Max Context Lines :')
         self.context_int = Entry(
                 frame_context, textvariable=self.context_lines, width=3)
 
@@ -2012,7 +2012,7 @@ def load_general_cfg(self):
         self.format_width.set(idleConf.GetOption(
                 'extensions', 'FormatParagraph', 'max-width', type='int'))
         self.context_lines.set(idleConf.GetOption(
-                'extensions', 'CodeContext', 'numlines', type='int'))
+                'extensions', 'CodeContext', 'maxlines', type='int'))
 
         # Set additional help sources.
         self.user_helplist = idleConf.GetAllExtraHelpSourcesList()
@@ -2204,6 +2204,9 @@ def detach(self):
 'opener' - opener '({[' corresponding to closer; 'parens' - both chars;
 'expression' (default) - also everything in between.  Flash-delay is how
 long to highlight if cursor is not moved (0 means forever).
+
+CodeContext: Maxlines is the maximum number of code context lines to
+display when Code Context is turned on for an editor window.
 '''
 }
 
diff --git a/Lib/idlelib/idle_test/test_codecontext.py b/Lib/idlelib/idle_test/test_codecontext.py
index ed45828641b4cd..4c775eee6b5fd1 100644
--- a/Lib/idlelib/idle_test/test_codecontext.py
+++ b/Lib/idlelib/idle_test/test_codecontext.py
@@ -110,7 +110,7 @@ def test_del(self):
 
     def test_reload(self):
         codecontext.CodeContext.reload()
-        self.assertEqual(self.cc.context_depth, 3)
+        self.assertEqual(self.cc.context_depth, 15)
 
     def test_toggle_code_context_event(self):
         eq = self.assertEqual
@@ -127,7 +127,7 @@ def test_toggle_code_context_event(self):
         eq(cc.label['font'], cc.textfont)
         eq(cc.label['fg'], cc.fgcolor)
         eq(cc.label['bg'], cc.bgcolor)
-        eq(cc.label['text'], '\n' * 2)
+        eq(cc.label['text'], '')
 
         # Toggle off.
         eq(toggle(), 'break')
@@ -193,24 +193,26 @@ def test_update_code_context(self):
         eq(cc.info, [(0, -1, '', False)])
         eq(cc.topvisible, 1)
 
+        # Scroll down to line 1.
+        cc.text.yview(1)
+        cc.update_code_context()
+        eq(cc.info, [(0, -1, '', False)])
+        eq(cc.topvisible, 2)
+        eq(cc.label['text'], '')
+
         # Scroll down to line 2.
         cc.text.yview(2)
         cc.update_code_context()
         eq(cc.info, [(0, -1, '', False), (2, 0, 'class C1():', 'class')])
         eq(cc.topvisible, 3)
-        # context_depth is 3 so it pads with blank lines.
-        eq(cc.label['text'], '\n'
-                             '\n'
-                             'class C1():')
+        eq(cc.label['text'], 'class C1():')
 
         # Scroll down to line 3.  Since it's a comment, nothing changes.
         cc.text.yview(3)
         cc.update_code_context()
         eq(cc.info, [(0, -1, '', False), (2, 0, 'class C1():', 'class')])
         eq(cc.topvisible, 4)
-        eq(cc.label['text'], '\n'
-                             '\n'
-                             'class C1():')
+        eq(cc.label['text'], 'class C1():')
 
         # Scroll down to line 4.
         cc.text.yview(4)
@@ -219,8 +221,7 @@ def test_update_code_context(self):
                      (2, 0, 'class C1():', 'class'),
                      (4, 4, '    def __init__(self, a, b):', 'def')])
         eq(cc.topvisible, 5)
-        eq(cc.label['text'], '\n'
-                             'class C1():\n'
+        eq(cc.label['text'], 'class C1():\n'
                              '    def __init__(self, a, b):')
 
         # Scroll down to line 11.  Last 'def' is removed.
@@ -232,7 +233,8 @@ def test_update_code_context(self):
                      (8, 8, '        if a > b:', 'if'),
                      (10, 8, '        elif a < b:', 'elif')])
         eq(cc.topvisible, 12)
-        eq(cc.label['text'], '    def compare(self):\n'
+        eq(cc.label['text'], 'class C1():\n'
+                             '    def compare(self):\n'
                              '        if a > b:\n'
                              '        elif a < b:')
 
@@ -245,7 +247,8 @@ def test_update_code_context(self):
                      (8, 8, '        if a > b:', 'if'),
                      (10, 8, '        elif a < b:', 'elif')])
         eq(cc.topvisible, 12)
-        eq(cc.label['text'], '    def compare(self):\n'
+        eq(cc.label['text'], 'class C1():\n'
+                             '    def compare(self):\n'
                              '        if a > b:\n'
                              '        elif a < b:')
 
diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py
index 982dc0b7eff7e1..26aba32c47e6ab 100644
--- a/Lib/idlelib/idle_test/test_configdialog.py
+++ b/Lib/idlelib/idle_test/test_configdialog.py
@@ -1170,7 +1170,7 @@ def test_paragraph(self):
     def test_context(self):
         self.page.context_int.delete(0, 'end')
         self.page.context_int.insert(0, '1')
-        self.assertEqual(extpage, {'CodeContext': {'numlines': '1'}})
+        self.assertEqual(extpage, {'CodeContext': {'maxlines': '1'}})
 
     def test_source_selected(self):
         d = self.page
diff --git a/Misc/NEWS.d/next/IDLE/2018-05-24-20-42-44.bpo-33642.J0VQbS.rst b/Misc/NEWS.d/next/IDLE/2018-05-24-20-42-44.bpo-33642.J0VQbS.rst
new file mode 100644
index 00000000000000..d8ecb3938d43c5
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2018-05-24-20-42-44.bpo-33642.J0VQbS.rst
@@ -0,0 +1,9 @@
+IDLE: Only display actual code context lines.
+Instead of displaying a fixed number of lines, some blank, Code Context
+now displays the variable number of actual context lines.  When there
+are no context lines, it shows a single blank line to indicate that the
+feature is turned on.
+
+The Code Context configuration option is changed from 'numlines'
+(default 3) to 'maxlines' (default 15) to avoid possible interference
+between user settings for the old and new versions of Code Context.



<!-- issue-number: bpo-33642 -->
https://bugs.python.org/issue33642
<!-- /issue-number -->
