Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

codecontainer.py 8.9KB

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