Skip to content

Commit 8506016

Browse files
csabellaterryjreedy
authored andcommitted
bpo-33628: IDLE: Minor code cleanup of codecontext.py and its tests (GH-7085)
1 parent 8ebf5ce commit 8506016

File tree

4 files changed

+62
-59
lines changed

4 files changed

+62
-59
lines changed

Lib/idlelib/NEWS.txt

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ Released on 2018-06-18?
33
======================================
44

55

6+
bpo-33628: Cleanup codecontext.py and its test.
7+
68
bpo-32831: Add docstrings and tests for codecontext.py.
79
Coverage is 100%. Patch by Cheryl Sabella.
810

Lib/idlelib/codecontext.py

+38-40
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,23 @@
2323
FONTUPDATEINTERVAL = 1000 # millisec
2424

2525

26-
def getspacesfirstword(s, c=re.compile(r"^(\s*)(\w*)")):
27-
"Extract the beginning whitespace and first word from s."
28-
return c.match(s).groups()
26+
def get_spaces_firstword(codeline, c=re.compile(r"^(\s*)(\w*)")):
27+
"Extract the beginning whitespace and first word from codeline."
28+
return c.match(codeline).groups()
29+
30+
31+
def get_line_info(codeline):
32+
"""Return tuple of (line indent value, codeline, block start keyword).
33+
34+
The indentation of empty lines (or comment lines) is INFINITY.
35+
If the line does not start a block, the keyword value is False.
36+
"""
37+
spaces, firstword = get_spaces_firstword(codeline)
38+
indent = len(spaces)
39+
if len(codeline) == indent or codeline[indent] == '#':
40+
indent = INFINITY
41+
opener = firstword in BLOCKOPENERS and firstword
42+
return indent, codeline, opener
2943

3044

3145
class CodeContext:
@@ -42,15 +56,15 @@ def __init__(self, editwin):
4256
self.textfont is the editor window font.
4357
4458
self.label displays the code context text above the editor text.
45-
Initially None it is toggled via <<toggle-code-context>>.
59+
Initially None, it is toggled via <<toggle-code-context>>.
4660
self.topvisible is the number of the top text line displayed.
4761
self.info is a list of (line number, indent level, line text,
4862
block keyword) tuples for the block structure above topvisible.
49-
s self.info[0] is initialized a 'dummy' line which
50-
# starts the toplevel 'block' of the module.
63+
self.info[0] is initialized with a 'dummy' line which
64+
starts the toplevel 'block' of the module.
5165
5266
self.t1 and self.t2 are two timer events on the editor text widget to
53-
monitor for changes to the context text or editor font.
67+
monitor for changes to the context text or editor font.
5468
"""
5569
self.editwin = editwin
5670
self.text = editwin.text
@@ -94,45 +108,28 @@ def toggle_code_context_event(self, event=None):
94108
# All values are passed through getint(), since some
95109
# values may be pixel objects, which can't simply be added to ints.
96110
widgets = self.editwin.text, self.editwin.text_frame
97-
# Calculate the required vertical padding
111+
# Calculate the required horizontal padding and border width.
98112
padx = 0
113+
border = 0
99114
for widget in widgets:
100115
padx += widget.tk.getint(widget.pack_info()['padx'])
101116
padx += widget.tk.getint(widget.cget('padx'))
102-
# Calculate the required border width
103-
border = 0
104-
for widget in widgets:
105117
border += widget.tk.getint(widget.cget('border'))
106118
self.label = tkinter.Label(
107119
self.editwin.top, text="\n" * (self.context_depth - 1),
108120
anchor=W, justify=LEFT, font=self.textfont,
109121
bg=self.bgcolor, fg=self.fgcolor,
110-
width=1, #don't request more than we get
122+
width=1, # Don't request more than we get.
111123
padx=padx, border=border, relief=SUNKEN)
112124
# Pack the label widget before and above the text_frame widget,
113-
# thus ensuring that it will appear directly above text_frame
125+
# thus ensuring that it will appear directly above text_frame.
114126
self.label.pack(side=TOP, fill=X, expand=False,
115127
before=self.editwin.text_frame)
116128
else:
117129
self.label.destroy()
118130
self.label = None
119131
return "break"
120132

121-
def get_line_info(self, linenum):
122-
"""Return tuple of (line indent value, text, and block start keyword).
123-
124-
If the line does not start a block, the keyword value is False.
125-
The indentation of empty lines (or comment lines) is INFINITY.
126-
"""
127-
text = self.text.get("%d.0" % linenum, "%d.end" % linenum)
128-
spaces, firstword = getspacesfirstword(text)
129-
opener = firstword in BLOCKOPENERS and firstword
130-
if len(text) == len(spaces) or text[len(spaces)] == '#':
131-
indent = INFINITY
132-
else:
133-
indent = len(spaces)
134-
return indent, text, opener
135-
136133
def get_context(self, new_topvisible, stopline=1, stopindent=0):
137134
"""Return a list of block line tuples and the 'last' indent.
138135
@@ -144,16 +141,17 @@ def get_context(self, new_topvisible, stopline=1, stopindent=0):
144141
"""
145142
assert stopline > 0
146143
lines = []
147-
# The indentation level we are currently in:
144+
# The indentation level we are currently in.
148145
lastindent = INFINITY
149146
# For a line to be interesting, it must begin with a block opening
150147
# keyword, and have less indentation than lastindent.
151148
for linenum in range(new_topvisible, stopline-1, -1):
152-
indent, text, opener = self.get_line_info(linenum)
149+
codeline = self.text.get(f'{linenum}.0', f'{linenum}.end')
150+
indent, text, opener = get_line_info(codeline)
153151
if indent < lastindent:
154152
lastindent = indent
155153
if opener in ("else", "elif"):
156-
# We also show the if statement
154+
# Also show the if statement.
157155
lastindent += 1
158156
if opener and linenum < new_topvisible and indent >= stopindent:
159157
lines.append((linenum, indent, text, opener))
@@ -172,19 +170,19 @@ def update_code_context(self):
172170
the context label.
173171
"""
174172
new_topvisible = int(self.text.index("@0,0").split('.')[0])
175-
if self.topvisible == new_topvisible: # haven't scrolled
173+
if self.topvisible == new_topvisible: # Haven't scrolled.
176174
return
177-
if self.topvisible < new_topvisible: # scroll down
175+
if self.topvisible < new_topvisible: # Scroll down.
178176
lines, lastindent = self.get_context(new_topvisible,
179177
self.topvisible)
180-
# retain only context info applicable to the region
181-
# between topvisible and new_topvisible:
178+
# Retain only context info applicable to the region
179+
# between topvisible and new_topvisible.
182180
while self.info[-1][1] >= lastindent:
183181
del self.info[-1]
184-
else: # self.topvisible > new_topvisible: # scroll up
182+
else: # self.topvisible > new_topvisible: # Scroll up.
185183
stopindent = self.info[-1][1] + 1
186-
# retain only context info associated
187-
# with lines above new_topvisible:
184+
# Retain only context info associated
185+
# with lines above new_topvisible.
188186
while self.info[-1][0] >= new_topvisible:
189187
stopindent = self.info[-1][1]
190188
del self.info[-1]
@@ -193,9 +191,9 @@ def update_code_context(self):
193191
stopindent)
194192
self.info.extend(lines)
195193
self.topvisible = new_topvisible
196-
# empty lines in context pane:
194+
# Empty lines in context pane.
197195
context_strings = [""] * max(0, self.context_depth - len(self.info))
198-
# followed by the context hint lines:
196+
# Followed by the context hint lines.
199197
context_strings += [x[2] for x in self.info[-self.context_depth:]]
200198
self.label["text"] = '\n'.join(context_strings)
201199

Lib/idlelib/idle_test/test_codecontext.py

+20-19
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,6 @@ def test_init(self):
9696
eq(self.root.tk.call('after', 'info', self.cc.t2)[1], 'timer')
9797

9898
def test_del(self):
99-
self.root.tk.call('after', 'info', self.cc.t1)
100-
self.root.tk.call('after', 'info', self.cc.t2)
10199
self.cc.__del__()
102100
with self.assertRaises(TclError) as msg:
103101
self.root.tk.call('after', 'info', self.cc.t1)
@@ -135,21 +133,6 @@ def test_toggle_code_context_event(self):
135133
eq(toggle(), 'break')
136134
self.assertIsNone(cc.label)
137135

138-
def test_get_line_info(self):
139-
eq = self.assertEqual
140-
gli = self.cc.get_line_info
141-
142-
# Line 1 is not a BLOCKOPENER.
143-
eq(gli(1), (codecontext.INFINITY, '', False))
144-
# Line 2 is a BLOCKOPENER without an indent.
145-
eq(gli(2), (0, 'class C1():', 'class'))
146-
# Line 3 is not a BLOCKOPENER and does not return the indent level.
147-
eq(gli(3), (codecontext.INFINITY, ' # Class comment.', False))
148-
# Line 4 is a BLOCKOPENER and is indented.
149-
eq(gli(4), (4, ' def __init__(self, a, b):', 'def'))
150-
# Line 8 is a different BLOCKOPENER and is indented.
151-
eq(gli(8), (8, ' if a > b:', 'if'))
152-
153136
def test_get_context(self):
154137
eq = self.assertEqual
155138
gc = self.cc.get_context
@@ -323,8 +306,8 @@ def test_font_timer_event(self):
323306

324307
class HelperFunctionText(unittest.TestCase):
325308

326-
def test_getspacesfirstword(self):
327-
get = codecontext.getspacesfirstword
309+
def test_get_spaces_firstword(self):
310+
get = codecontext.get_spaces_firstword
328311
test_lines = (
329312
(' first word', (' ', 'first')),
330313
('\tfirst word', ('\t', 'first')),
@@ -342,6 +325,24 @@ def test_getspacesfirstword(self):
342325
c=re.compile(r'^(\s*)([^\s]*)')),
343326
(' ', '(continuation)'))
344327

328+
def test_get_line_info(self):
329+
eq = self.assertEqual
330+
gli = codecontext.get_line_info
331+
lines = code_sample.splitlines()
332+
333+
# Line 1 is not a BLOCKOPENER.
334+
eq(gli(lines[0]), (codecontext.INFINITY, '', False))
335+
# Line 2 is a BLOCKOPENER without an indent.
336+
eq(gli(lines[1]), (0, 'class C1():', 'class'))
337+
# Line 3 is not a BLOCKOPENER and does not return the indent level.
338+
eq(gli(lines[2]), (codecontext.INFINITY, ' # Class comment.', False))
339+
# Line 4 is a BLOCKOPENER and is indented.
340+
eq(gli(lines[3]), (4, ' def __init__(self, a, b):', 'def'))
341+
# Line 8 is a different BLOCKOPENER and is indented.
342+
eq(gli(lines[7]), (8, ' if a > b:', 'if'))
343+
# Test tab.
344+
eq(gli('\tif a == b:'), (1, '\tif a == b:', 'if'))
345+
345346

346347
if __name__ == '__main__':
347348
unittest.main(verbosity=2)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
IDLE: Cleanup codecontext.py and its test.
2+

0 commit comments

Comments
 (0)