22
22
UPDATEINTERVAL = 100 # millisec
23
23
FONTUPDATEINTERVAL = 1000 # millisec
24
24
25
+
25
26
def getspacesfirstword (s , c = re .compile (r"^(\s*)(\w*)" )):
27
+ "Extract the beginning whitespace and first word from s."
26
28
return c .match (s ).groups ()
27
29
28
30
29
31
class CodeContext :
32
+ "Display block context above the edit window."
33
+
30
34
bgcolor = "LightGray"
31
35
fgcolor = "Black"
32
36
33
37
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
+ """
34
55
self .editwin = editwin
35
56
self .text = editwin .text
36
57
self .textfont = self .text ["font" ]
37
58
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 )]
44
59
self .topvisible = 1
60
+ self .info = [(0 , - 1 , "" , False )]
45
61
# Start two update cycles, one for context lines, one for font changes.
46
62
self .t1 = self .text .after (UPDATEINTERVAL , self .timer_event )
47
63
self .t2 = self .text .after (FONTUPDATEINTERVAL , self .font_timer_event )
48
64
49
65
@classmethod
50
66
def reload (cls ):
67
+ "Load class variables from config."
51
68
cls .context_depth = idleConf .GetOption ("extensions" , "CodeContext" ,
52
69
"numlines" , type = "int" , default = 3 )
53
70
## cls.bgcolor = idleConf.GetOption("extensions", "CodeContext",
@@ -56,13 +73,20 @@ def reload(cls):
56
73
## "fgcolor", type="str", default="Black")
57
74
58
75
def __del__ (self ):
76
+ "Cancel scheduled events."
59
77
try :
60
78
self .text .after_cancel (self .t1 )
61
79
self .text .after_cancel (self .t2 )
62
80
except :
63
81
pass
64
82
65
83
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
+ """
66
90
if not self .label :
67
91
# Calculate the border width and horizontal padding required to
68
92
# align the context with the text in the main Text widget.
@@ -95,11 +119,10 @@ def toggle_code_context_event(self, event=None):
95
119
return "break"
96
120
97
121
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).
99
123
100
124
If the line does not start a block, the keyword value is False.
101
125
The indentation of empty lines (or comment lines) is INFINITY.
102
-
103
126
"""
104
127
text = self .text .get ("%d.0" % linenum , "%d.end" % linenum )
105
128
spaces , firstword = getspacesfirstword (text )
@@ -111,11 +134,13 @@ def get_line_info(self, linenum):
111
134
return indent , text , opener
112
135
113
136
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.
118
138
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.
119
144
"""
120
145
assert stopline > 0
121
146
lines = []
@@ -140,6 +165,11 @@ def get_context(self, new_topvisible, stopline=1, stopindent=0):
140
165
def update_code_context (self ):
141
166
"""Update context information and lines visible in the context pane.
142
167
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.
143
173
"""
144
174
new_topvisible = int (self .text .index ("@0,0" ).split ('.' )[0 ])
145
175
if self .topvisible == new_topvisible : # haven't scrolled
@@ -151,7 +181,7 @@ def update_code_context(self):
151
181
# between topvisible and new_topvisible:
152
182
while self .info [- 1 ][1 ] >= lastindent :
153
183
del self .info [- 1 ]
154
- elif self .topvisible > new_topvisible : # scroll up
184
+ else : # self.topvisible > new_topvisible: # scroll up
155
185
stopindent = self .info [- 1 ][1 ] + 1
156
186
# retain only context info associated
157
187
# with lines above new_topvisible:
@@ -170,11 +200,13 @@ def update_code_context(self):
170
200
self .label ["text" ] = '\n ' .join (context_strings )
171
201
172
202
def timer_event (self ):
203
+ "Event on editor text widget triggered every UPDATEINTERVAL ms."
173
204
if self .label :
174
205
self .update_code_context ()
175
206
self .t1 = self .text .after (UPDATEINTERVAL , self .timer_event )
176
207
177
208
def font_timer_event (self ):
209
+ "Event on editor text widget triggered every FONTUPDATEINTERVAL ms."
178
210
newtextfont = self .text ["font" ]
179
211
if self .label and newtextfont != self .textfont :
180
212
self .textfont = newtextfont
@@ -183,3 +215,8 @@ def font_timer_event(self):
183
215
184
216
185
217
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