Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / devtools / v8plus / lib / python2.4 / idlelib / CodeContext.py
CommitLineData
920dae64
AT
1"""CodeContext - Display the block context of code at top of edit window
2
3Once code has scrolled off the top of the screen, it can be difficult
4to determine which block you are in. This extension implements a pane
5at the top of each IDLE edit window which provides block structure
6hints. These hints are the lines which contain the block opening
7keywords, e.g. 'if', for the enclosing block. The number of hint lines
8is determined by the numlines variable in the CodeContext section of
9config-extensions.def. Lines which do not open blocks are not shown in
10the context hints pane.
11
12"""
13import Tkinter
14from configHandler import idleConf
15from sets import Set
16import re
17
18BLOCKOPENERS = Set(["class", "def", "elif", "else", "except", "finally", "for",
19 "if", "try", "while"])
20INFINITY = 1 << 30
21UPDATEINTERVAL = 100 # millisec
22FONTUPDATEINTERVAL = 1000 # millisec
23
24getspacesfirstword = lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups()
25
26class CodeContext:
27 menudefs = [('options', [('!Code Conte_xt', '<<toggle-code-context>>')])]
28
29 numlines = idleConf.GetOption("extensions", "CodeContext",
30 "numlines", type="int", default=3)
31 bgcolor = idleConf.GetOption("extensions", "CodeContext",
32 "bgcolor", type="str", default="LightGray")
33 fgcolor = idleConf.GetOption("extensions", "CodeContext",
34 "fgcolor", type="str", default="Black")
35 def __init__(self, editwin):
36 self.editwin = editwin
37 self.text = editwin.text
38 self.textfont = self.text["font"]
39 self.label = None
40 # Dummy line, which starts the "block" of the whole document:
41 self.info = list(self.interesting_lines(1))
42 self.lastfirstline = 1
43 visible = idleConf.GetOption("extensions", "CodeContext",
44 "visible", type="bool", default=False)
45 if visible:
46 self.toggle_code_context_event()
47 self.editwin.setvar('<<toggle-code-context>>', True)
48 # Start two update cycles, one for context lines, one for font changes.
49 self.text.after(UPDATEINTERVAL, self.timer_event)
50 self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
51
52 def toggle_code_context_event(self, event=None):
53 if not self.label:
54 self.label = Tkinter.Label(self.editwin.top,
55 text="\n" * (self.numlines - 1),
56 anchor="w", justify="left",
57 font=self.textfont,
58 bg=self.bgcolor, fg=self.fgcolor,
59 relief="sunken",
60 width=1, # Don't request more than we get
61 )
62 self.label.pack(side="top", fill="x", expand=0,
63 after=self.editwin.status_bar)
64 else:
65 self.label.destroy()
66 self.label = None
67 idleConf.SetOption("extensions", "CodeContext", "visible",
68 str(self.label is not None))
69 idleConf.SaveUserCfgFiles()
70
71 def get_line_info(self, linenum):
72 """Get the line indent value, text, and any block start keyword
73
74 If the line does not start a block, the keyword value is False.
75 The indentation of empty lines (or comment lines) is INFINITY.
76 There is a dummy block start, with indentation -1 and text "".
77
78 Return the indent level, text (including leading whitespace),
79 and the block opening keyword.
80
81 """
82 if linenum == 0:
83 return -1, "", True
84 text = self.text.get("%d.0" % linenum, "%d.end" % linenum)
85 spaces, firstword = getspacesfirstword(text)
86 opener = firstword in BLOCKOPENERS and firstword
87 if len(text) == len(spaces) or text[len(spaces)] == '#':
88 indent = INFINITY
89 else:
90 indent = len(spaces)
91 return indent, text, opener
92
93 def interesting_lines(self, firstline):
94 """Generator which yields context lines, starting at firstline."""
95 # The indentation level we are currently in:
96 lastindent = INFINITY
97 # For a line to be interesting, it must begin with a block opening
98 # keyword, and have less indentation than lastindent.
99 for line_index in xrange(firstline, -1, -1):
100 indent, text, opener = self.get_line_info(line_index)
101 if indent < lastindent:
102 lastindent = indent
103 if opener in ("else", "elif"):
104 # We also show the if statement
105 lastindent += 1
106 if opener and line_index < firstline:
107 yield line_index, text
108
109 def update_label(self):
110 firstline = int(self.text.index("@0,0").split('.')[0])
111 if self.lastfirstline == firstline:
112 return
113 self.lastfirstline = firstline
114 tmpstack = []
115 for line_index, text in self.interesting_lines(firstline):
116 # Remove irrelevant self.info items, and when we reach a relevant
117 # item (which must happen because of the dummy element), break.
118 while self.info[-1][0] > line_index:
119 del self.info[-1]
120 if self.info[-1][0] == line_index:
121 break
122 tmpstack.append((line_index, text))
123 while tmpstack:
124 self.info.append(tmpstack.pop())
125 lines = [""] * max(0, self.numlines - len(self.info)) + \
126 [x[1] for x in self.info[-self.numlines:]]
127 self.label["text"] = '\n'.join(lines)
128
129 def timer_event(self):
130 if self.label:
131 self.update_label()
132 self.text.after(UPDATEINTERVAL, self.timer_event)
133
134 def font_timer_event(self):
135 newtextfont = self.text["font"]
136 if self.label and newtextfont != self.textfont:
137 self.textfont = newtextfont
138 self.label["font"] = self.textfont
139 self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)