symbian-qemu-0.9.1-12/python-2.6.1/Lib/idlelib/CodeContext.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 """CodeContext - Extension to display the block context above the edit window
       
     2 
       
     3 Once code has scrolled off the top of a window, it can be difficult to
       
     4 determine which block you are in.  This extension implements a pane at the top
       
     5 of each IDLE edit window which provides block structure hints.  These hints are
       
     6 the lines which contain the block opening keywords, e.g. 'if', for the
       
     7 enclosing block.  The number of hint lines is determined by the numlines
       
     8 variable in the CodeContext section of config-extensions.def. Lines which do
       
     9 not open blocks are not shown in the context hints pane.
       
    10 
       
    11 """
       
    12 import Tkinter
       
    13 from Tkconstants import TOP, LEFT, X, W, SUNKEN
       
    14 from configHandler import idleConf
       
    15 import re
       
    16 from sys import maxint as INFINITY
       
    17 
       
    18 BLOCKOPENERS = set(["class", "def", "elif", "else", "except", "finally", "for",
       
    19                     "if", "try", "while", "with"])
       
    20 UPDATEINTERVAL = 100 # millisec
       
    21 FONTUPDATEINTERVAL = 1000 # millisec
       
    22 
       
    23 getspacesfirstword =\
       
    24                    lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups()
       
    25 
       
    26 class CodeContext:
       
    27     menudefs = [('options', [('!Code Conte_xt', '<<toggle-code-context>>')])]
       
    28     context_depth = idleConf.GetOption("extensions", "CodeContext",
       
    29                                        "numlines", type="int", default=3)
       
    30     bgcolor = idleConf.GetOption("extensions", "CodeContext",
       
    31                                  "bgcolor", type="str", default="LightGray")
       
    32     fgcolor = idleConf.GetOption("extensions", "CodeContext",
       
    33                                  "fgcolor", type="str", default="Black")
       
    34     def __init__(self, editwin):
       
    35         self.editwin = editwin
       
    36         self.text = editwin.text
       
    37         self.textfont = self.text["font"]
       
    38         self.label = None
       
    39         # self.info is a list of (line number, indent level, line text, block
       
    40         # keyword) tuples providing the block structure associated with
       
    41         # self.topvisible (the linenumber of the line displayed at the top of
       
    42         # the edit window). self.info[0] is initialized as a 'dummy' line which
       
    43         # starts the toplevel 'block' of the module.
       
    44         self.info = [(0, -1, "", False)]
       
    45         self.topvisible = 1
       
    46         visible = idleConf.GetOption("extensions", "CodeContext",
       
    47                                      "visible", type="bool", default=False)
       
    48         if visible:
       
    49             self.toggle_code_context_event()
       
    50             self.editwin.setvar('<<toggle-code-context>>', True)
       
    51         # Start two update cycles, one for context lines, one for font changes.
       
    52         self.text.after(UPDATEINTERVAL, self.timer_event)
       
    53         self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
       
    54 
       
    55     def toggle_code_context_event(self, event=None):
       
    56         if not self.label:
       
    57             # Calculate the border width and horizontal padding required to
       
    58             # align the context with the text in the main Text widget.
       
    59             #
       
    60             # All values are passed through int(str(<value>)), since some
       
    61             # values may be pixel objects, which can't simply be added to ints.
       
    62             widgets = self.editwin.text, self.editwin.text_frame
       
    63             # Calculate the required vertical padding
       
    64             padx = 0
       
    65             for widget in widgets:
       
    66                 padx += int(str( widget.pack_info()['padx'] ))
       
    67                 padx += int(str( widget.cget('padx') ))
       
    68             # Calculate the required border width
       
    69             border = 0
       
    70             for widget in widgets:
       
    71                 border += int(str( widget.cget('border') ))
       
    72             self.label = Tkinter.Label(self.editwin.top,
       
    73                                        text="\n" * (self.context_depth - 1),
       
    74                                        anchor=W, justify=LEFT,
       
    75                                        font=self.textfont,
       
    76                                        bg=self.bgcolor, fg=self.fgcolor,
       
    77                                        width=1, #don't request more than we get
       
    78                                        padx=padx, border=border,
       
    79                                        relief=SUNKEN)
       
    80             # Pack the label widget before and above the text_frame widget,
       
    81             # thus ensuring that it will appear directly above text_frame
       
    82             self.label.pack(side=TOP, fill=X, expand=False,
       
    83                             before=self.editwin.text_frame)
       
    84         else:
       
    85             self.label.destroy()
       
    86             self.label = None
       
    87         idleConf.SetOption("extensions", "CodeContext", "visible",
       
    88                            str(self.label is not None))
       
    89         idleConf.SaveUserCfgFiles()
       
    90 
       
    91     def get_line_info(self, linenum):
       
    92         """Get the line indent value, text, and any block start keyword
       
    93 
       
    94         If the line does not start a block, the keyword value is False.
       
    95         The indentation of empty lines (or comment lines) is INFINITY.
       
    96 
       
    97         """
       
    98         text = self.text.get("%d.0" % linenum, "%d.end" % linenum)
       
    99         spaces, firstword = getspacesfirstword(text)
       
   100         opener = firstword in BLOCKOPENERS and firstword
       
   101         if len(text) == len(spaces) or text[len(spaces)] == '#':
       
   102             indent = INFINITY
       
   103         else:
       
   104             indent = len(spaces)
       
   105         return indent, text, opener
       
   106 
       
   107     def get_context(self, new_topvisible, stopline=1, stopindent=0):
       
   108         """Get context lines, starting at new_topvisible and working backwards.
       
   109 
       
   110         Stop when stopline or stopindent is reached. Return a tuple of context
       
   111         data and the indent level at the top of the region inspected.
       
   112 
       
   113         """
       
   114         assert stopline > 0
       
   115         lines = []
       
   116         # The indentation level we are currently in:
       
   117         lastindent = INFINITY
       
   118         # For a line to be interesting, it must begin with a block opening
       
   119         # keyword, and have less indentation than lastindent.
       
   120         for linenum in xrange(new_topvisible, stopline-1, -1):
       
   121             indent, text, opener = self.get_line_info(linenum)
       
   122             if indent < lastindent:
       
   123                 lastindent = indent
       
   124                 if opener in ("else", "elif"):
       
   125                     # We also show the if statement
       
   126                     lastindent += 1
       
   127                 if opener and linenum < new_topvisible and indent >= stopindent:
       
   128                     lines.append((linenum, indent, text, opener))
       
   129                 if lastindent <= stopindent:
       
   130                     break
       
   131         lines.reverse()
       
   132         return lines, lastindent
       
   133 
       
   134     def update_code_context(self):
       
   135         """Update context information and lines visible in the context pane.
       
   136 
       
   137         """
       
   138         new_topvisible = int(self.text.index("@0,0").split('.')[0])
       
   139         if self.topvisible == new_topvisible:      # haven't scrolled
       
   140             return
       
   141         if self.topvisible < new_topvisible:       # scroll down
       
   142             lines, lastindent = self.get_context(new_topvisible,
       
   143                                                  self.topvisible)
       
   144             # retain only context info applicable to the region
       
   145             # between topvisible and new_topvisible:
       
   146             while self.info[-1][1] >= lastindent:
       
   147                 del self.info[-1]
       
   148         elif self.topvisible > new_topvisible:     # scroll up
       
   149             stopindent = self.info[-1][1] + 1
       
   150             # retain only context info associated
       
   151             # with lines above new_topvisible:
       
   152             while self.info[-1][0] >= new_topvisible:
       
   153                 stopindent = self.info[-1][1]
       
   154                 del self.info[-1]
       
   155             lines, lastindent = self.get_context(new_topvisible,
       
   156                                                  self.info[-1][0]+1,
       
   157                                                  stopindent)
       
   158         self.info.extend(lines)
       
   159         self.topvisible = new_topvisible
       
   160         # empty lines in context pane:
       
   161         context_strings = [""] * max(0, self.context_depth - len(self.info))
       
   162         # followed by the context hint lines:
       
   163         context_strings += [x[2] for x in self.info[-self.context_depth:]]
       
   164         self.label["text"] = '\n'.join(context_strings)
       
   165 
       
   166     def timer_event(self):
       
   167         if self.label:
       
   168             self.update_code_context()
       
   169         self.text.after(UPDATEINTERVAL, self.timer_event)
       
   170 
       
   171     def font_timer_event(self):
       
   172         newtextfont = self.text["font"]
       
   173         if self.label and newtextfont != self.textfont:
       
   174             self.textfont = newtextfont
       
   175             self.label["font"] = self.textfont
       
   176         self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)