123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- """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
- from . 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 as 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))
|