Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 33 additions & 38 deletions Lib/idlelib/codecontext.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@

BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for",
"if", "try", "while", "with", "async"}
UPDATEINTERVAL = 100 # millisec
CONFIGUPDATEINTERVAL = 1000 # millisec


def get_spaces_firstword(codeline, c=re.compile(r"^(\s*)(\w*)")):
Expand All @@ -44,13 +42,13 @@ def get_line_info(codeline):

class CodeContext:
"Display block context above the edit window."
UPDATEINTERVAL = 100 # millisec

def __init__(self, editwin):
"""Initialize settings for context block.

editwin is the Editor window for the context block.
self.text is the editor window text widget.
self.textfont is the editor window font.

self.context displays the code context text above the editor text.
Initially None, it is toggled via <<toggle-code-context>>.
Expand All @@ -65,29 +63,26 @@ def __init__(self, editwin):
"""
self.editwin = editwin
self.text = editwin.text
self.textfont = self.text["font"]
self.contextcolors = CodeContext.colors
self.context = None
self.topvisible = 1
self.info = [(0, -1, "", False)]
# Start two update cycles, one for context lines, one for font changes.
self.t1 = self.text.after(UPDATEINTERVAL, self.timer_event)
self.t2 = self.text.after(CONFIGUPDATEINTERVAL, self.config_timer_event)
self.t1 = None

@classmethod
def reload(cls):
"Load class variables from config."
cls.context_depth = idleConf.GetOption("extensions", "CodeContext",
"maxlines", type="int", default=15)
cls.colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'context')
"maxlines", type="int",
default=15)

def __del__(self):
"Cancel scheduled events."
try:
self.text.after_cancel(self.t1)
self.text.after_cancel(self.t2)
except:
pass
if self.t1 is not None:
try:
self.text.after_cancel(self.t1)
except tkinter.TclError:
pass
self.t1 = None

def toggle_code_context_event(self, event=None):
"""Toggle code context display.
Expand All @@ -96,7 +91,7 @@ def toggle_code_context_event(self, event=None):
window text (toggle on). If it does exist, destroy it (toggle off).
Return 'break' to complete the processing of the binding.
"""
if not self.context:
if self.context is None:
# Calculate the border width and horizontal padding required to
# align the context with the text in the main Text widget.
#
Expand All @@ -111,21 +106,23 @@ def toggle_code_context_event(self, event=None):
padx += widget.tk.getint(widget.cget('padx'))
border += widget.tk.getint(widget.cget('border'))
self.context = tkinter.Text(
self.editwin.top, font=self.textfont,
bg=self.contextcolors['background'],
fg=self.contextcolors['foreground'],
height=1,
width=1, # Don't request more than we get.
padx=padx, border=border, relief=SUNKEN, state='disabled')
self.editwin.top, font=self.text['font'],
height=1,
width=1, # Don't request more than we get.
padx=padx, border=border, relief=SUNKEN, state='disabled')
self.update_highlight_colors()
self.context.bind('<ButtonRelease-1>', self.jumptoline)
# Pack the context widget before and above the text_frame widget,
# thus ensuring that it will appear directly above text_frame.
self.context.pack(side=TOP, fill=X, expand=False,
before=self.editwin.text_frame)
before=self.editwin.text_frame)
menu_status = 'Hide'
self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event)
else:
self.context.destroy()
self.context = None
self.text.after_cancel(self.t1)
self.t1 = None
menu_status = 'Show'
self.editwin.update_menu_label(menu='options', index='* Code Context',
label=f'{menu_status} Code Context')
Expand Down Expand Up @@ -169,7 +166,7 @@ def update_code_context(self):
be retrieved and the context area will be updated with the code,
up to the number of maxlines.
"""
new_topvisible = int(self.text.index("@0,0").split('.')[0])
new_topvisible = self.editwin.getlineno("@0,0")
if self.topvisible == new_topvisible: # Haven't scrolled.
return
if self.topvisible < new_topvisible: # Scroll down.
Expand Down Expand Up @@ -217,21 +214,19 @@ def jumptoline(self, event=None):

def timer_event(self):
"Event on editor text widget triggered every UPDATEINTERVAL ms."
if self.context:
if self.context is not None:
self.update_code_context()
self.t1 = self.text.after(UPDATEINTERVAL, self.timer_event)

def config_timer_event(self):
"Event on editor text widget triggered every CONFIGUPDATEINTERVAL ms."
newtextfont = self.text["font"]
if (self.context and (newtextfont != self.textfont or
CodeContext.colors != self.contextcolors)):
self.textfont = newtextfont
self.contextcolors = CodeContext.colors
self.context["font"] = self.textfont
self.context['background'] = self.contextcolors['background']
self.context['foreground'] = self.contextcolors['foreground']
self.t2 = self.text.after(CONFIGUPDATEINTERVAL, self.config_timer_event)
self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event)

def update_font(self, font):
if self.context is not None:
self.context['font'] = font

def update_highlight_colors(self):
if self.context is not None:
colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'context')
self.context['background'] = colors['background']
self.context['foreground'] = colors['foreground']


CodeContext.reload()
Expand Down
19 changes: 16 additions & 3 deletions Lib/idlelib/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ class EditorWindow(object):
filesystemencoding = sys.getfilesystemencoding() # for file names
help_url = None

allow_codecontext = True

def __init__(self, flist=None, filename=None, key=None, root=None):
# Delay import: runscript imports pyshell imports EditorWindow.
from idlelib.runscript import ScriptBinding
Expand Down Expand Up @@ -247,6 +249,7 @@ def __init__(self, flist=None, filename=None, key=None, root=None):
self.good_load = False
self.set_indentation_params(False)
self.color = None # initialized below in self.ResetColorizer
self.codecontext = None
if filename:
if os.path.exists(filename) and not os.path.isdir(filename):
if io.loadfile(filename):
Expand Down Expand Up @@ -312,8 +315,10 @@ def __init__(self, flist=None, filename=None, key=None, root=None):
text.bind("<<refresh-calltip>>", ctip.refresh_calltip_event)
text.bind("<<force-open-calltip>>", ctip.force_open_calltip_event)
text.bind("<<zoom-height>>", self.ZoomHeight(self).zoom_height_event)
text.bind("<<toggle-code-context>>",
self.CodeContext(self).toggle_code_context_event)
if self.allow_codecontext:
self.codecontext = self.CodeContext(self)
text.bind("<<toggle-code-context>>",
self.codecontext.toggle_code_context_event)

def _filename_to_unicode(self, filename):
"""Return filename as BMP unicode so displayable in Tk."""
Expand Down Expand Up @@ -773,6 +778,9 @@ def ResetColorizer(self):
self._addcolorizer()
EditorWindow.color_config(self.text)

if self.codecontext is not None:
self.codecontext.update_highlight_colors()

IDENTCHARS = string.ascii_letters + string.digits + "_"

def colorize_syntax_error(self, text, pos):
Expand All @@ -790,7 +798,12 @@ def ResetFont(self):
"Update the text widgets' font if it is changed"
# Called from configdialog.py

self.text['font'] = idleConf.GetFont(self.root, 'main','EditorWindow')
new_font = idleConf.GetFont(self.root, 'main', 'EditorWindow')
# Update the code context widget first, since its height affects
# the height of the text widget. This avoids double re-rendering.
if self.codecontext is not None:
self.codecontext.update_font(new_font)
self.text['font'] = new_font

def RemoveKeybindings(self):
"Remove the keybindings before they are changed."
Expand Down
Loading