bpo-37530: simplify, optimize and clean up IDLE code context (GH-14675) · python/cpython@bb79ab8

@@ -19,8 +19,6 @@

19192020

BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for",

2121

"if", "try", "while", "with", "async"}

22-

UPDATEINTERVAL = 100 # millisec

23-

CONFIGUPDATEINTERVAL = 1000 # millisec

242225232624

def get_spaces_firstword(codeline, c=re.compile(r"^(\s*)(\w*)")):

@@ -44,13 +42,13 @@ def get_line_info(codeline):

44424543

class CodeContext:

4644

"Display block context above the edit window."

45+

UPDATEINTERVAL = 100 # millisec

47464847

def __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

"""

6664

self.editwin = editwin

6765

self.text = editwin.text

68-

self.textfont = self.text["font"]

69-

self.contextcolors = CodeContext.colors

7066

self.context = None

7167

self.topvisible = 1

7268

self.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

7872

def reload(cls):

7973

"Load class variables from config."

8074

cls.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)

83778478

def __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

91869287

def 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):

111106

padx += widget.tk.getint(widget.cget('padx'))

112107

border += widget.tk.getint(widget.cget('border'))

113108

self.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()

120114

self.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.

123117

self.context.pack(side=TOP, fill=X, expand=False,

124-

before=self.editwin.text_frame)

118+

before=self.editwin.text_frame)

125119

menu_status = 'Hide'

120+

self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event)

126121

else:

127122

self.context.destroy()

128123

self.context = None

124+

self.text.after_cancel(self.t1)

125+

self.t1 = None

129126

menu_status = 'Show'

130127

self.editwin.update_menu_label(menu='options', index='* Code Context',

131128

label=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")

173170

if self.topvisible == new_topvisible: # Haven't scrolled.

174171

return

175172

if self.topvisible < new_topvisible: # Scroll down.

@@ -217,21 +214,19 @@ def jumptoline(self, event=None):

217214218215

def timer_event(self):

219216

"Event on editor text widget triggered every UPDATEINTERVAL ms."

220-

if self.context:

217+

if self.context is not None:

221218

self.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']

235230236231237232

CodeContext.reload()