codecontainer.py
8.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
"""A utility class for a code container.
A code container is a class which holds source code for a debugger. It knows how
to color the text, and also how to translate lines into offsets, and back.
"""
import sys
from win32com.axdebug import axdebug
import tokenize
from util import RaiseNotImpl, _wrap
from win32com.server.exception import Exception
import win32api, winerror
import contexts
_keywords = {} # set of Python keywords
for name in """
and assert break class continue def del elif else except exec
finally for from global if import in is lambda not
or pass print raise return try while
""".split():
_keywords[name] = 1
class SourceCodeContainer:
def __init__(self, text, fileName = "<Remove Me!>", sourceContext = 0, startLineNumber = 0, site = None, debugDocument = None):
self.sourceContext = sourceContext # The source context added by a smart host.
self.text = text
if text:
self._buildlines()
self.nextLineNo = 0
self.fileName = fileName
self.codeContexts = {}
self.site = site
self.startLineNumber = startLineNumber
self.debugDocument = None
def _Close(self):
self.text = self.lines = self.lineOffsets = None
self.codeContexts = None
self.debugDocument = None
self.site = None
self.sourceContext = None
def GetText(self):
return self.text
def GetName(self, dnt):
assert 0, "You must subclass this"
def GetFileName(self):
return self.fileName
def GetPositionOfLine(self, cLineNumber):
self.GetText() # Prime us.
try:
return self.lineOffsets[cLineNumber]
except IndexError:
raise Exception(scode=winerror.S_FALSE)
def GetLineOfPosition(self, charPos):
self.GetText() # Prime us.
lastOffset = 0
lineNo = 0
for lineOffset in self.lineOffsets[1:]:
if lineOffset > charPos:
break
lastOffset = lineOffset
lineNo = lineNo + 1
else: # for not broken.
# print "Cant find", charPos, "in", self.lineOffsets
raise Exception(scode=winerror.S_FALSE)
# print "GLOP ret=",lineNo, (charPos-lastOffset)
return lineNo, (charPos-lastOffset)
def GetNextLine(self):
if self.nextLineNo>=len(self.lines):
self.nextLineNo = 0 # auto-reset.
return ""
rc = self.lines[self.nextLineNo]
self.nextLineNo = self.nextLineNo + 1
return rc
def GetLine(self, num):
self.GetText() # Prime us.
return self.lines[num]
def GetNumChars(self):
return len(self.GetText())
def GetNumLines(self):
self.GetText() # Prime us.
return len(self.lines)
def _buildline(self, pos):
i = self.text.find('\n', pos)
if i < 0:
newpos = len(self.text)
else:
newpos = i+1
r = self.text[pos:newpos]
return r, newpos
def _buildlines(self):
self.lines = []
self.lineOffsets = [0]
line, pos = self._buildline(0)
while line:
self.lines.append(line)
self.lineOffsets.append(pos)
line, pos = self._buildline(pos)
def _ProcessToken(self, type, token, spos, epos, line):
srow, scol = spos
erow, ecol = epos
self.GetText() # Prime us.
linenum = srow - 1 # Lines zero based for us too.
realCharPos = self.lineOffsets[linenum] + scol
numskipped = realCharPos - self.lastPos
if numskipped==0:
pass
elif numskipped==1:
self.attrs.append(axdebug.SOURCETEXT_ATTR_COMMENT)
else:
self.attrs.append((axdebug.SOURCETEXT_ATTR_COMMENT, numskipped))
kwSize = len(token)
self.lastPos = realCharPos + kwSize
attr = 0
if type==tokenize.NAME:
if token in _keywords:
attr = axdebug.SOURCETEXT_ATTR_KEYWORD
elif type==tokenize.STRING:
attr = axdebug.SOURCETEXT_ATTR_STRING
elif type==tokenize.NUMBER:
attr = axdebug.SOURCETEXT_ATTR_NUMBER
elif type==tokenize.OP:
attr = axdebug.SOURCETEXT_ATTR_OPERATOR
elif type==tokenize.COMMENT:
attr = axdebug.SOURCETEXT_ATTR_COMMENT
# else attr remains zero...
if kwSize==0:
pass
elif kwSize==1:
self.attrs.append(attr)
else:
self.attrs.append((attr, kwSize))
def GetSyntaxColorAttributes(self):
self.lastPos = 0
self.attrs = []
try:
tokenize.tokenize(self.GetNextLine, self._ProcessToken)
except tokenize.TokenError:
pass # Ignore - will cause all subsequent text to be commented.
numAtEnd = len(self.GetText()) - self.lastPos
if numAtEnd:
self.attrs.append((axdebug.SOURCETEXT_ATTR_COMMENT, numAtEnd))
return self.attrs
# We also provide and manage DebugDocumentContext objects
def _MakeDebugCodeContext(self, lineNo, charPos, len):
return _wrap(contexts.DebugCodeContext(lineNo, charPos, len, self, self.site), axdebug.IID_IDebugCodeContext)
# Make a context at the given position. It should take up the entire context.
def _MakeContextAtPosition(self, charPos):
lineNo, offset = self.GetLineOfPosition(charPos)
try:
endPos = self.GetPositionOfLine(lineNo+1)
except:
endPos = charPos
codecontext = self._MakeDebugCodeContext(lineNo, charPos, endPos-charPos)
return codecontext
# Returns a DebugCodeContext. debugDocument can be None for smart hosts.
def GetCodeContextAtPosition(self, charPos):
# trace("GetContextOfPos", charPos, maxChars)
# Convert to line number.
lineNo, offset = self.GetLineOfPosition(charPos)
charPos = self.GetPositionOfLine(lineNo)
try:
cc = self.codeContexts[charPos]
# trace(" GetContextOfPos using existing")
except KeyError:
cc = self._MakeContextAtPosition(charPos)
self.codeContexts[charPos] = cc
return cc
class SourceModuleContainer(SourceCodeContainer):
def __init__(self, module):
self.module = module
if hasattr(module, '__file__'):
fname = self.module.__file__
# Check for .pyc or .pyo or even .pys!
if fname[-1] in ['O','o','C','c', 'S', 's']: fname = fname[:-1]
try:
fname = win32api.GetFullPathName(fname)
except win32api.error:
pass
else:
if module.__name__=='__main__' and len(sys.argv)>0:
fname = sys.argv[0]
else:
fname = "<Unknown!>"
SourceCodeContainer.__init__(self, None, fname)
def GetText(self):
if self.text is None:
fname = self.GetFileName()
if fname:
try:
self.text = open(fname, "r").read()
except IOError, details:
self.text = "# Exception opening file\n# %s" % (repr(details))
else:
self.text = "# No file available for module '%s'" % (self.module)
self._buildlines()
return self.text
def GetName(self, dnt):
name = self.module.__name__
try:
fname = win32api.GetFullPathName(self.module.__file__)
except win32api.error:
fname = self.module.__file__
except AttributeError:
fname = name
if dnt==axdebug.DOCUMENTNAMETYPE_APPNODE:
return name.split(".")[-1]
elif dnt==axdebug.DOCUMENTNAMETYPE_TITLE:
return fname
elif dnt==axdebug.DOCUMENTNAMETYPE_FILE_TAIL:
return os.path.split(fname)[1]
elif dnt==axdebug.DOCUMENTNAMETYPE_URL:
return "file:%s" % fname
else:
raise Exception(scode=winerror.E_UNEXPECTED)
if __name__=='__main__':
import sys
sys.path.append(".")
import ttest
sc = SourceModuleContainer(ttest)
# sc = SourceCodeContainer(open(sys.argv[1], "rb").read(), sys.argv[1])
attrs = sc.GetSyntaxColorAttributes()
attrlen = 0
for attr in attrs:
if type(attr)==type(()):
attrlen = attrlen + attr[1]
else:
attrlen = attrlen + 1
text = sc.GetText()
if attrlen!=len(text):
print "Lengths dont match!!! (%d/%d)" % (attrlen, len(text))
# print "Attributes:"
# print attrs
print "GetLineOfPos=", sc.GetLineOfPosition(0)
print "GetLineOfPos=", sc.GetLineOfPosition(4)
print "GetLineOfPos=", sc.GetLineOfPosition(10)