diff options
author | Cheryl Sabella <cheryl.sabella@gmail.com> | 2018-05-19 19:34:03 (GMT) |
---|---|---|
committer | Terry Jan Reedy <tjreedy@udel.edu> | 2018-05-19 19:34:03 (GMT) |
commit | 654038d896d78a8373b60184f335acd516215acd (patch) | |
tree | 8e65f193192c1dbb71fd3f6a27ad92edb04aea77 /Lib/idlelib/codecontext.py | |
parent | cf8abcbe0310ab4b3eb8b66ae795878b9df1a8ac (diff) | |
download | cpython-654038d896d78a8373b60184f335acd516215acd.zip cpython-654038d896d78a8373b60184f335acd516215acd.tar.gz cpython-654038d896d78a8373b60184f335acd516215acd.tar.bz2 |
bpo-32831: IDLE: Add docstrings and tests for codecontext (GH-5638)
Diffstat (limited to 'Lib/idlelib/codecontext.py')
-rw-r--r-- | Lib/idlelib/codecontext.py | 63 |
1 files changed, 50 insertions, 13 deletions
diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 2bfb2e9..efd163e 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -22,32 +22,49 @@ BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for", UPDATEINTERVAL = 100 # millisec FONTUPDATEINTERVAL = 1000 # millisec + def getspacesfirstword(s, c=re.compile(r"^(\s*)(\w*)")): + "Extract the beginning whitespace and first word from s." 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", @@ -56,6 +73,7 @@ class CodeContext: ## "fgcolor", type="str", default="Black") def __del__(self): + "Cancel scheduled events." try: self.text.after_cancel(self.t1) self.text.after_cancel(self.t2) @@ -63,6 +81,12 @@ class CodeContext: 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. @@ -95,11 +119,10 @@ class CodeContext: 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). 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) @@ -111,11 +134,13 @@ class CodeContext: 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 = [] @@ -140,6 +165,11 @@ class CodeContext: 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 @@ -151,7 +181,7 @@ class CodeContext: # 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: @@ -170,11 +200,13 @@ class CodeContext: 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 @@ -183,3 +215,8 @@ class CodeContext: CodeContext.reload() + + +if __name__ == "__main__": # pragma: no cover + import unittest + unittest.main('idlelib.idle_test.test_codecontext', verbosity=2, exit=False) |