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
63 changes: 50 additions & 13 deletions Lib/idlelib/codecontext.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,49 @@
UPDATEINTERVAL = 100 # millisec
FONTUPDATEINTERVAL = 1000 # millisec


def getspacesfirstword(s, c=re.compile(r"^(\s*)(\w*)")):
"Extract the beginning whitespace and first word from s."

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we do a code cleanup pass, get_spaces_firstword and replace s with codeline.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Including this in the code cleanup PR.

return c.match(s).groups()


class CodeContext:
"Display block context above the edit window."

bgcolor = "LightGray"
fgcolor = "Black"

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.label displays the code context text above the editor text.
Initially None it is toggled via <<toggle-code-context>>.
self.topvisible is the number of the top text line displayed.
self.info is a list of (line number, indent level, line text,
block keyword) tuples for the block structure above topvisible.
s self.info[0] is initialized a 'dummy' line which
# starts the toplevel 'block' of the module.

self.t1 and self.t2 are two timer events on the editor text widget to
monitor for changes to the context text or editor font.
"""
self.editwin = editwin
self.text = editwin.text
self.textfont = self.text["font"]
self.label = None
# self.info is a list of (line number, indent level, line text, block
# keyword) tuples providing the block structure associated with
# self.topvisible (the linenumber of the line displayed at the top of
# the edit window). self.info[0] is initialized as a 'dummy' line which
# starts the toplevel 'block' of the module.
self.info = [(0, -1, "", False)]
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(FONTUPDATEINTERVAL, self.font_timer_event)

@classmethod
def reload(cls):
"Load class variables from config."
cls.context_depth = idleConf.GetOption("extensions", "CodeContext",
"numlines", type="int", default=3)
## cls.bgcolor = idleConf.GetOption("extensions", "CodeContext",
Expand All @@ -56,13 +73,20 @@ def reload(cls):
## "fgcolor", type="str", default="Black")

def __del__(self):
"Cancel scheduled events."
try:
self.text.after_cancel(self.t1)
self.text.after_cancel(self.t2)
except:
pass

def toggle_code_context_event(self, event=None):
"""Toggle code context display.

If self.label doesn't exist, create it to match the size of the editor
window text (toggle on). If it does exist, destroy it (toggle off).
Return 'break' to complete the processing of the binding.
"""
if not self.label:
# Calculate the border width and horizontal padding required to
# align the context with the text in the main Text widget.
Expand Down Expand Up @@ -95,11 +119,10 @@ def toggle_code_context_event(self, event=None):
return "break"

def get_line_info(self, linenum):
"""Get the line indent value, text, and any block start keyword
"""Return tuple of (line indent value, text, and block start keyword).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we removed self and added codeline as a parameter, this would become a helper function and testable without the cc fixture.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Including this in the code cleanup PR.


If the line does not start a block, the keyword value is False.
The indentation of empty lines (or comment lines) is INFINITY.

"""
text = self.text.get("%d.0" % linenum, "%d.end" % linenum)
spaces, firstword = getspacesfirstword(text)
Expand All @@ -111,11 +134,13 @@ def get_line_info(self, linenum):
return indent, text, opener

def get_context(self, new_topvisible, stopline=1, stopindent=0):
"""Get context lines, starting at new_topvisible and working backwards.

Stop when stopline or stopindent is reached. Return a tuple of context
data and the indent level at the top of the region inspected.
"""Return a list of block line tuples and the 'last' indent.

The tuple fields are (linenum, indent, text, opener).
The list represents header lines from new_topvisible back to
stopline with successively shorter indents > stopindent.
The list is returned ordered by line number.
Last indent returned is the smallest indent observed.
"""
assert stopline > 0
lines = []
Expand All @@ -140,6 +165,11 @@ def get_context(self, new_topvisible, stopline=1, stopindent=0):
def update_code_context(self):
"""Update context information and lines visible in the context pane.

No update is done if the text hasn't been scrolled. If the text
was scrolled, the lines that should be shown in the context will
be retrieved and the label widget will be updated with the code,
padded with blank lines so that the code appears on the bottom of
the context label.
"""
new_topvisible = int(self.text.index("@0,0").split('.')[0])
if self.topvisible == new_topvisible: # haven't scrolled
Expand All @@ -151,7 +181,7 @@ def update_code_context(self):
# between topvisible and new_topvisible:
while self.info[-1][1] >= lastindent:
del self.info[-1]
elif self.topvisible > new_topvisible: # scroll up
else: # self.topvisible > new_topvisible: # scroll up
stopindent = self.info[-1][1] + 1
# retain only context info associated
# with lines above new_topvisible:
Expand All @@ -170,11 +200,13 @@ def update_code_context(self):
self.label["text"] = '\n'.join(context_strings)

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

def font_timer_event(self):
"Event on editor text widget triggered every FONTUPDATEINTERVAL ms."
newtextfont = self.text["font"]
if self.label and newtextfont != self.textfont:
self.textfont = newtextfont
Expand All @@ -183,3 +215,8 @@ def font_timer_event(self):


CodeContext.reload()


if __name__ == "__main__": # pragma: no cover
import unittest
unittest.main('idlelib.idle_test.test_codecontext', verbosity=2, exit=False)
Loading