bpo-37530: simplify, optimize and clean up IDLE code context (GH-14675) · python/cpython@bb79ab8
@@ -19,8 +19,6 @@
19192020BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for",
2121"if", "try", "while", "with", "async"}
22-UPDATEINTERVAL = 100 # millisec
23-CONFIGUPDATEINTERVAL = 1000 # millisec
242225232624def get_spaces_firstword(codeline, c=re.compile(r"^(\s*)(\w*)")):
@@ -44,13 +42,13 @@ def get_line_info(codeline):
44424543class CodeContext:
4644"Display block context above the edit window."
45+UPDATEINTERVAL = 100 # millisec
47464847def __init__(self, editwin):
4948"""Initialize settings for context block.
50495150 editwin is the Editor window for the context block.
5251 self.text is the editor window text widget.
53- self.textfont is the editor window font.
54525553 self.context displays the code context text above the editor text.
5654 Initially None, it is toggled via <<toggle-code-context>>.
@@ -65,29 +63,26 @@ def __init__(self, editwin):
6563 """
6664self.editwin = editwin
6765self.text = editwin.text
68-self.textfont = self.text["font"]
69-self.contextcolors = CodeContext.colors
7066self.context = None
7167self.topvisible = 1
7268self.info = [(0, -1, "", False)]
73-# Start two update cycles, one for context lines, one for font changes.
74-self.t1 = self.text.after(UPDATEINTERVAL, self.timer_event)
75-self.t2 = self.text.after(CONFIGUPDATEINTERVAL, self.config_timer_event)
69+self.t1 = None
76707771@classmethod
7872def reload(cls):
7973"Load class variables from config."
8074cls.context_depth = idleConf.GetOption("extensions", "CodeContext",
81-"maxlines", type="int", default=15)
82-cls.colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'context')
75+ "maxlines", type="int",
76+ default=15)
83778478def __del__(self):
8579"Cancel scheduled events."
86-try:
87-self.text.after_cancel(self.t1)
88-self.text.after_cancel(self.t2)
89-except:
90-pass
80+if self.t1 is not None:
81+try:
82+self.text.after_cancel(self.t1)
83+except tkinter.TclError:
84+pass
85+self.t1 = None
91869287def toggle_code_context_event(self, event=None):
9388"""Toggle code context display.
@@ -96,7 +91,7 @@ def toggle_code_context_event(self, event=None):
9691 window text (toggle on). If it does exist, destroy it (toggle off).
9792 Return 'break' to complete the processing of the binding.
9893 """
99-if not self.context:
94+if self.context is None:
10095# Calculate the border width and horizontal padding required to
10196# align the context with the text in the main Text widget.
10297#
@@ -111,21 +106,23 @@ def toggle_code_context_event(self, event=None):
111106padx += widget.tk.getint(widget.cget('padx'))
112107border += widget.tk.getint(widget.cget('border'))
113108self.context = tkinter.Text(
114-self.editwin.top, font=self.textfont,
115-bg=self.contextcolors['background'],
116-fg=self.contextcolors['foreground'],
117-height=1,
118-width=1, # Don't request more than we get.
119-padx=padx, border=border, relief=SUNKEN, state='disabled')
109+self.editwin.top, font=self.text['font'],
110+height=1,
111+width=1, # Don't request more than we get.
112+padx=padx, border=border, relief=SUNKEN, state='disabled')
113+self.update_highlight_colors()
120114self.context.bind('<ButtonRelease-1>', self.jumptoline)
121115# Pack the context widget before and above the text_frame widget,
122116# thus ensuring that it will appear directly above text_frame.
123117self.context.pack(side=TOP, fill=X, expand=False,
124-before=self.editwin.text_frame)
118+ before=self.editwin.text_frame)
125119menu_status = 'Hide'
120+self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event)
126121else:
127122self.context.destroy()
128123self.context = None
124+self.text.after_cancel(self.t1)
125+self.t1 = None
129126menu_status = 'Show'
130127self.editwin.update_menu_label(menu='options', index='* Code Context',
131128label=f'{menu_status} Code Context')
@@ -169,7 +166,7 @@ def update_code_context(self):
169166 be retrieved and the context area will be updated with the code,
170167 up to the number of maxlines.
171168 """
172-new_topvisible = int(self.text.index("@0,0").split('.')[0])
169+new_topvisible = self.editwin.getlineno("@0,0")
173170if self.topvisible == new_topvisible: # Haven't scrolled.
174171return
175172if self.topvisible < new_topvisible: # Scroll down.
@@ -217,21 +214,19 @@ def jumptoline(self, event=None):
217214218215def timer_event(self):
219216"Event on editor text widget triggered every UPDATEINTERVAL ms."
220-if self.context:
217+if self.context is not None:
221218self.update_code_context()
222-self.t1 = self.text.after(UPDATEINTERVAL, self.timer_event)
223-224-def config_timer_event(self):
225-"Event on editor text widget triggered every CONFIGUPDATEINTERVAL ms."
226-newtextfont = self.text["font"]
227-if (self.context and (newtextfont != self.textfont or
228-CodeContext.colors != self.contextcolors)):
229-self.textfont = newtextfont
230-self.contextcolors = CodeContext.colors
231-self.context["font"] = self.textfont
232-self.context['background'] = self.contextcolors['background']
233-self.context['foreground'] = self.contextcolors['foreground']
234-self.t2 = self.text.after(CONFIGUPDATEINTERVAL, self.config_timer_event)
219+self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event)
220+221+def update_font(self, font):
222+if self.context is not None:
223+self.context['font'] = font
224+225+def update_highlight_colors(self):
226+if self.context is not None:
227+colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'context')
228+self.context['background'] = colors['background']
229+self.context['foreground'] = colors['foreground']
235230236231237232CodeContext.reload()