2222UPDATEINTERVAL = 100 # millisec
2323FONTUPDATEINTERVAL = 1000 # millisec
2424
25+
2526def 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
2931class 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
185217CodeContext .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