Skip to content

Commit 654038d

Browse files
csabellaterryjreedy
authored andcommitted
bpo-32831: IDLE: Add docstrings and tests for codecontext (GH-5638)
1 parent cf8abcb commit 654038d

File tree

3 files changed

+398
-13
lines changed

3 files changed

+398
-13
lines changed

Lib/idlelib/codecontext.py

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,32 +22,49 @@
2222
UPDATEINTERVAL = 100 # millisec
2323
FONTUPDATEINTERVAL = 1000 # millisec
2424

25+
2526
def getspacesfirstword(s, c=re.compile(r"^(\s*)(\w*)")):
27+
"Extract the beginning whitespace and first word from s."
2628
return c.match(s).groups()
2729

2830

2931
class CodeContext:
32+
"Display block context above the edit window."
33+
3034
bgcolor = "LightGray"
3135
fgcolor = "Black"
3236

3337
def __init__(self, editwin):
38+
"""Initialize settings for context block.
39+
40+
editwin is the Editor window for the context block.
41+
self.text is the editor window text widget.
42+
self.textfont is the editor window font.
43+
44+
self.label displays the code context text above the editor text.
45+
Initially None it is toggled via <<toggle-code-context>>.
46+
self.topvisible is the number of the top text line displayed.
47+
self.info is a list of (line number, indent level, line text,
48+
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.
51+
52+
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.
54+
"""
3455
self.editwin = editwin
3556
self.text = editwin.text
3657
self.textfont = self.text["font"]
3758
self.label = None
38-
# self.info is a list of (line number, indent level, line text, block
39-
# keyword) tuples providing the block structure associated with
40-
# self.topvisible (the linenumber of the line displayed at the top of
41-
# the edit window). self.info[0] is initialized as a 'dummy' line which
42-
# starts the toplevel 'block' of the module.
43-
self.info = [(0, -1, "", False)]
4459
self.topvisible = 1
60+
self.info = [(0, -1, "", False)]
4561
# Start two update cycles, one for context lines, one for font changes.
4662
self.t1 = self.text.after(UPDATEINTERVAL, self.timer_event)
4763
self.t2 = self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
4864

4965
@classmethod
5066
def reload(cls):
67+
"Load class variables from config."
5168
cls.context_depth = idleConf.GetOption("extensions", "CodeContext",
5269
"numlines", type="int", default=3)
5370
## cls.bgcolor = idleConf.GetOption("extensions", "CodeContext",
@@ -56,13 +73,20 @@ def reload(cls):
5673
## "fgcolor", type="str", default="Black")
5774

5875
def __del__(self):
76+
"Cancel scheduled events."
5977
try:
6078
self.text.after_cancel(self.t1)
6179
self.text.after_cancel(self.t2)
6280
except:
6381
pass
6482

6583
def toggle_code_context_event(self, event=None):
84+
"""Toggle code context display.
85+
86+
If self.label doesn't exist, create it to match the size of the editor
87+
window text (toggle on). If it does exist, destroy it (toggle off).
88+
Return 'break' to complete the processing of the binding.
89+
"""
6690
if not self.label:
6791
# Calculate the border width and horizontal padding required to
6892
# align the context with the text in the main Text widget.
@@ -95,11 +119,10 @@ def toggle_code_context_event(self, event=None):
95119
return "break"
96120

97121
def get_line_info(self, linenum):
98-
"""Get the line indent value, text, and any block start keyword
122+
"""Return tuple of (line indent value, text, and block start keyword).
99123
100124
If the line does not start a block, the keyword value is False.
101125
The indentation of empty lines (or comment lines) is INFINITY.
102-
103126
"""
104127
text = self.text.get("%d.0" % linenum, "%d.end" % linenum)
105128
spaces, firstword = getspacesfirstword(text)
@@ -111,11 +134,13 @@ def get_line_info(self, linenum):
111134
return indent, text, opener
112135

113136
def get_context(self, new_topvisible, stopline=1, stopindent=0):
114-
"""Get context lines, starting at new_topvisible and working backwards.
115-
116-
Stop when stopline or stopindent is reached. Return a tuple of context
117-
data and the indent level at the top of the region inspected.
137+
"""Return a list of block line tuples and the 'last' indent.
118138
139+
The tuple fields are (linenum, indent, text, opener).
140+
The list represents header lines from new_topvisible back to
141+
stopline with successively shorter indents > stopindent.
142+
The list is returned ordered by line number.
143+
Last indent returned is the smallest indent observed.
119144
"""
120145
assert stopline > 0
121146
lines = []
@@ -140,6 +165,11 @@ def get_context(self, new_topvisible, stopline=1, stopindent=0):
140165
def update_code_context(self):
141166
"""Update context information and lines visible in the context pane.
142167
168+
No update is done if the text hasn't been scrolled. If the text
169+
was scrolled, the lines that should be shown in the context will
170+
be retrieved and the label widget will be updated with the code,
171+
padded with blank lines so that the code appears on the bottom of
172+
the context label.
143173
"""
144174
new_topvisible = int(self.text.index("@0,0").split('.')[0])
145175
if self.topvisible == new_topvisible: # haven't scrolled
@@ -151,7 +181,7 @@ def update_code_context(self):
151181
# between topvisible and new_topvisible:
152182
while self.info[-1][1] >= lastindent:
153183
del self.info[-1]
154-
elif self.topvisible > new_topvisible: # scroll up
184+
else: # self.topvisible > new_topvisible: # scroll up
155185
stopindent = self.info[-1][1] + 1
156186
# retain only context info associated
157187
# with lines above new_topvisible:
@@ -170,11 +200,13 @@ def update_code_context(self):
170200
self.label["text"] = '\n'.join(context_strings)
171201

172202
def timer_event(self):
203+
"Event on editor text widget triggered every UPDATEINTERVAL ms."
173204
if self.label:
174205
self.update_code_context()
175206
self.t1 = self.text.after(UPDATEINTERVAL, self.timer_event)
176207

177208
def font_timer_event(self):
209+
"Event on editor text widget triggered every FONTUPDATEINTERVAL ms."
178210
newtextfont = self.text["font"]
179211
if self.label and newtextfont != self.textfont:
180212
self.textfont = newtextfont
@@ -183,3 +215,8 @@ def font_timer_event(self):
183215

184216

185217
CodeContext.reload()
218+
219+
220+
if __name__ == "__main__": # pragma: no cover
221+
import unittest
222+
unittest.main('idlelib.idle_test.test_codecontext', verbosity=2, exit=False)

0 commit comments

Comments
 (0)