PyXR

c:\python24\lib \ idlelib \ CodeContext.py



0001 """CodeContext - Display the block context of code at top of edit window
0002 
0003 Once code has scrolled off the top of the screen, it can be difficult
0004 to determine which block you are in.  This extension implements a pane
0005 at the top of each IDLE edit window which provides block structure
0006 hints.  These hints are the lines which contain the block opening
0007 keywords, e.g. 'if', for the enclosing block.  The number of hint lines
0008 is determined by the numlines variable in the CodeContext section of
0009 config-extensions.def. Lines which do not open blocks are not shown in
0010 the context hints pane.
0011 
0012 """
0013 import Tkinter
0014 from configHandler import idleConf
0015 from sets import Set
0016 import re
0017 
0018 BLOCKOPENERS = Set(["class", "def", "elif", "else", "except", "finally", "for",
0019                     "if", "try", "while"])
0020 INFINITY = 1 << 30
0021 UPDATEINTERVAL = 100 # millisec
0022 FONTUPDATEINTERVAL = 1000 # millisec
0023 
0024 getspacesfirstword = lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups()
0025 
0026 class CodeContext:
0027     menudefs = [('options', [('!Code Conte_xt', '<<toggle-code-context>>')])]
0028 
0029     numlines = idleConf.GetOption("extensions", "CodeContext",
0030                                   "numlines", type="int", default=3)
0031     bgcolor = idleConf.GetOption("extensions", "CodeContext",
0032                                  "bgcolor", type="str", default="LightGray")
0033     fgcolor = idleConf.GetOption("extensions", "CodeContext",
0034                                  "fgcolor", type="str", default="Black")
0035     def __init__(self, editwin):
0036         self.editwin = editwin
0037         self.text = editwin.text
0038         self.textfont = self.text["font"]
0039         self.label = None
0040         # Dummy line, which starts the "block" of the whole document:
0041         self.info = list(self.interesting_lines(1))
0042         self.lastfirstline = 1
0043         visible = idleConf.GetOption("extensions", "CodeContext",
0044                                      "visible", type="bool", default=False)
0045         if visible:
0046             self.toggle_code_context_event()
0047             self.editwin.setvar('<<toggle-code-context>>', True)
0048         # Start two update cycles, one for context lines, one for font changes.
0049         self.text.after(UPDATEINTERVAL, self.timer_event)
0050         self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
0051 
0052     def toggle_code_context_event(self, event=None):
0053         if not self.label:
0054             self.label = Tkinter.Label(self.editwin.top,
0055                                       text="\n" * (self.numlines - 1),
0056                                       anchor="w", justify="left",
0057                                       font=self.textfont,
0058                                       bg=self.bgcolor, fg=self.fgcolor,
0059                                       relief="sunken",
0060                                       width=1, # Don't request more than we get
0061                                       )
0062             self.label.pack(side="top", fill="x", expand=0,
0063                             after=self.editwin.status_bar)
0064         else:
0065             self.label.destroy()
0066             self.label = None
0067         idleConf.SetOption("extensions", "CodeContext", "visible",
0068                            str(self.label is not None))
0069         idleConf.SaveUserCfgFiles()
0070 
0071     def get_line_info(self, linenum):
0072         """Get the line indent value, text, and any block start keyword
0073 
0074         If the line does not start a block, the keyword value is False.
0075         The indentation of empty lines (or comment lines) is INFINITY.
0076         There is a dummy block start, with indentation -1 and text "".
0077 
0078         Return the indent level, text (including leading whitespace),
0079         and the block opening keyword.
0080 
0081         """
0082         if linenum == 0:
0083             return -1, "", True
0084         text = self.text.get("%d.0" % linenum, "%d.end" % linenum)
0085         spaces, firstword = getspacesfirstword(text)
0086         opener = firstword in BLOCKOPENERS and firstword
0087         if len(text) == len(spaces) or text[len(spaces)] == '#':
0088             indent = INFINITY
0089         else:
0090             indent = len(spaces)
0091         return indent, text, opener
0092 
0093     def interesting_lines(self, firstline):
0094         """Generator which yields context lines, starting at firstline."""
0095         # The indentation level we are currently in:
0096         lastindent = INFINITY
0097         # For a line to be interesting, it must begin with a block opening
0098         # keyword, and have less indentation than lastindent.
0099         for line_index in xrange(firstline, -1, -1):
0100             indent, text, opener = self.get_line_info(line_index)
0101             if indent < lastindent:
0102                 lastindent = indent
0103                 if opener in ("else", "elif"):
0104                     # We also show the if statement
0105                     lastindent += 1
0106                 if opener and line_index < firstline:
0107                     yield line_index, text
0108 
0109     def update_label(self):
0110         firstline = int(self.text.index("@0,0").split('.')[0])
0111         if self.lastfirstline == firstline:
0112             return
0113         self.lastfirstline = firstline
0114         tmpstack = []
0115         for line_index, text in self.interesting_lines(firstline):
0116             # Remove irrelevant self.info items, and when we reach a relevant
0117             # item (which must happen because of the dummy element), break.
0118             while self.info[-1][0] > line_index:
0119                 del self.info[-1]
0120             if self.info[-1][0] == line_index:
0121                 break
0122             tmpstack.append((line_index, text))
0123         while tmpstack:
0124             self.info.append(tmpstack.pop())
0125         lines = [""] * max(0, self.numlines - len(self.info)) + \
0126                 [x[1] for x in self.info[-self.numlines:]]
0127         self.label["text"] = '\n'.join(lines)
0128 
0129     def timer_event(self):
0130         if self.label:
0131             self.update_label()
0132         self.text.after(UPDATEINTERVAL, self.timer_event)
0133 
0134     def font_timer_event(self):
0135         newtextfont = self.text["font"]
0136         if self.label and newtextfont != self.textfont:
0137             self.textfont = newtextfont
0138             self.label["font"] = self.textfont
0139         self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
0140 

Generated by PyXR 0.9.4
SourceForge.net Logo