PageRenderTime 63ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/src/codeintel/support/gencix/javascript/prototype/prototype_to_cix.py

https://gitlab.com/Smileyt/KomodoEdit
Python | 295 lines | 230 code | 5 blank | 60 comment | 3 complexity | 029a697d5efebe1b7021516fc0aa0987 MD5 | raw file
  1. #!/usr/bin/env python
  2. # ***** BEGIN LICENSE BLOCK *****
  3. # Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4. #
  5. # The contents of this file are subject to the Mozilla Public License
  6. # Version 1.1 (the "License"); you may not use this file except in
  7. # compliance with the License. You may obtain a copy of the License at
  8. # http://www.mozilla.org/MPL/
  9. #
  10. # Software distributed under the License is distributed on an "AS IS"
  11. # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
  12. # License for the specific language governing rights and limitations
  13. # under the License.
  14. #
  15. # The Original Code is Komodo code.
  16. #
  17. # The Initial Developer of the Original Code is ActiveState Software Inc.
  18. # Portions created by ActiveState Software Inc are Copyright (C) 2000-2007
  19. # ActiveState Software Inc. All Rights Reserved.
  20. #
  21. # Contributor(s):
  22. # ActiveState Software Inc
  23. #
  24. # Alternatively, the contents of this file may be used under the terms of
  25. # either the GNU General Public License Version 2 or later (the "GPL"), or
  26. # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27. # in which case the provisions of the GPL or the LGPL are applicable instead
  28. # of those above. If you wish to allow use of your version of this file only
  29. # under the terms of either the GPL or the LGPL, and not to allow others to
  30. # use your version of this file under the terms of the MPL, indicate your
  31. # decision by deleting the provisions above and replace them with the notice
  32. # and other provisions required by the GPL or the LGPL. If you do not delete
  33. # the provisions above, a recipient may use your version of this file under
  34. # the terms of any one of the MPL, the GPL or the LGPL.
  35. #
  36. # ***** END LICENSE BLOCK *****
  37. #
  38. # Contributers (aka Blame):
  39. # - Todd Whiteman
  40. #
  41. """ Prototype documentation to Komodo CIX parser.
  42. Command line tool that parses up prototype's own HTML documentation to
  43. produce a Komodo CIX file. Works by grabbing latest copy of prototype online
  44. documentation and then parsing the html to produce "prototype.cix".
  45. Requirements:
  46. * BeautifulSoup (http://www.crummy.com/software/BeautifulSoup/)
  47. * cElementTree (http://effbot.org/downloads/#cElementTree)
  48. Webpage used for documentation:
  49. * http://www.sergiopereira.com/articles/prototype.js.html
  50. * (local copy should be within this directory as well)
  51. Tested with prototype versions:
  52. * 1.4.0 (default)
  53. """
  54. import os
  55. import sys
  56. import urllib
  57. from pprint import pprint
  58. from optparse import OptionParser
  59. from BeautifulSoup import BeautifulSoup, NavigableString
  60. from codeintel2.gencix_utils import *
  61. #def getSubElementText(elem):
  62. # result = []
  63. # for i in elem:
  64. # result.append(i.string)
  65. # return condenseSpaces("".join(result))
  66. def getSubElementText(elem):
  67. """Return all the text of elem child elements"""
  68. return condenseSpaces(''.join([e for e in elem.recursiveChildGenerator()
  69. if isinstance(e,unicode)]))
  70. def processTableMethods(cix_element, table_tag):
  71. # Table rows: [u'Method', u'Kind', u'Arguments', u'Description']
  72. for table_row in table_tag.findAll("tr"):
  73. row_tags = table_row.findAll("td")
  74. if row_tags:
  75. assert(len(row_tags) == 4)
  76. method = row_tags[0].string
  77. msplit = method.split("(", 1)
  78. method_name = msplit[0]
  79. isConstructor = False
  80. if method_name == "[ctor]":
  81. method_name = cix_element.get("name")
  82. isConstructor = True
  83. cix_function = createCixFunction(cix_element, method_name)
  84. if isConstructor:
  85. addCixAttribute(cix_function, "__ctor__")
  86. setCixSignature(cix_function, "%s(%s" % (method_name, msplit[1]))
  87. setCixDoc(cix_function, getSubElementText(row_tags[3]), parse=True)
  88. def processTableProperties(cix_element, table_tag):
  89. # Table rows: [u'Property', u'Type', u'Description']
  90. # Table rows: [u'Property', u'Type', u'Kind', u'Description']
  91. for table_row in table_tag.findAll("tr"):
  92. row_tags = table_row.findAll("td")
  93. if row_tags:
  94. assert(len(row_tags) in (3, 4))
  95. name = row_tags[0].string
  96. #print "name: %r" % name
  97. #print "type: %r" % getSubElementText(row_tags[1]).split("(")[0]
  98. #print "doc: %r" % getSubElementText(row_tags[-1])
  99. cix_variable = createCixVariable(cix_element, name)
  100. vartype = standardizeJSType(getSubElementText(row_tags[1]).split("(")[0])
  101. addCixType(cix_variable, vartype)
  102. setCixDoc(cix_variable, getSubElementText(row_tags[-1]), parse=True)
  103. #print
  104. def processTableTag(cix_element, table_tag):
  105. header_tags = [ tag.string for tag in table_tag.findAll("th") ]
  106. #print header_tags
  107. if "Method" in header_tags:
  108. processTableMethods(cix_element, table_tag)
  109. elif "Property" in header_tags:
  110. processTableProperties(cix_element, table_tag)
  111. else:
  112. raise "Unknown header tags: %r" % (header_tags)
  113. def processScopeFields(cix_element, h4_tag):
  114. nextsib = h4_tag
  115. while 1:
  116. nextsib = nextsib.nextSibling
  117. if not nextsib:
  118. break
  119. elif isinstance(nextsib, NavigableString):
  120. pass
  121. elif nextsib.name in ("h4", "a"):
  122. break
  123. elif nextsib.name == "p":
  124. for table_tag in nextsib.findAll("table"):
  125. processTableTag(cix_element, table_tag)
  126. # Objects, classes, variables already created
  127. cix_scopes = {}
  128. def processScope(cix_module, h4_tag):
  129. spans = h4_tag.findAll("span", limit=1)
  130. if len(spans) == 1:
  131. h4_text = getSubElementText(h4_tag)
  132. h4_text_split = h4_text.split()
  133. scopeName = spans[0].string
  134. scopeNames = scopeName.split(".")
  135. if len(scopeNames) > 1:
  136. # Find the scope for this
  137. parentScopeName = ".".join(scopeNames[:-1])
  138. cix_module = cix_scopes.get(parentScopeName, None)
  139. if cix_module is None:
  140. raise "Could not find scope: %r for: %r" % (parentScopeName, scopeName)
  141. if h4_text_split[0] == "The" and h4_text_split[-1] == "class":
  142. print "Class:",
  143. cix_element = createCixClass(cix_module, scopeNames[-1])
  144. else:
  145. print "Object:",
  146. cix_element = createCixVariable(cix_module, scopeNames[-1])
  147. cix_scopes[scopeName] = cix_element
  148. print "%s - %s" % (scopeName, h4_text)
  149. processScopeFields(cix_element, h4_tag)
  150. def processRefTags(cix_module, p_tag):
  151. for h4_tag in p_tag.findNextSiblings("h4"):
  152. #print sibling
  153. processScope(cix_module, h4_tag)
  154. cix_info_from_name = {
  155. "$": { "returnType" : "Element", # Can also return an Array when called with multiples
  156. "args" : [("elementId", "String")],
  157. "signature" : "$(elementId [, ...]) --> Element"
  158. },
  159. "$F": { "returnType" : "String",
  160. "args" : [("element", "Element")],
  161. "signature" : "$F(element/elementId]) --> String"
  162. },
  163. "$A": { "returnType" : "Array",
  164. "args" : [("obj", "Object")],
  165. "signature" : "$A(obj) --> Array"
  166. },
  167. "$H": { "returnType" : "Hash",
  168. "args" : [("obj", "Object")],
  169. "signature" : "$H(obj) --> Hash"
  170. },
  171. "$R": { "returnType" : "ObjectRange",
  172. "args" : [("lowerBound", "Number"),
  173. ("upperBound", "Number"),
  174. ("excludeBounds", "Boolean"),],
  175. "signature" : "$R(lowerBound, upperBound, excludeBounds) --> ObjectRange"
  176. },
  177. "Try.these": { "returnType" : None,
  178. "args" : [("func1", "Function")],
  179. "signature" : "these(func1, [, ...])"
  180. },
  181. }
  182. def processH4Tag(cix_module, h4_tag):
  183. # These are all utility functions:
  184. # <h4>Using the <span class="functionName">$()</span> function</h4>
  185. # <p>
  186. # The <span class="code">$()</span> function is a handy shortcut to the all-too-frequent <span class="code">document.getElementById()</span> function
  187. # of the DOM. Like the DOM function, this one returns the element that has the id passed as an argument.
  188. # </p>
  189. span_tags = h4_tag.findAll("span", attrs={"class": "functionName"})
  190. for span_tag in span_tags:
  191. nextsib = h4_tag.nextSibling
  192. while isinstance(nextsib, NavigableString):
  193. nextsib = nextsib.nextSibling
  194. p_tag = nextsib
  195. #print "p_tag:", p_tag
  196. if p_tag.name == "p":
  197. cix_element = cix_module
  198. # We have enough info now
  199. signature = span_tag.string
  200. method_name = signature.rstrip("() ")
  201. # We can probably do better manually here
  202. info = cix_info_from_name.get(method_name)
  203. cix_variable = None
  204. sp = method_name.split(".")
  205. if len(sp) > 1:
  206. for name in sp[:-1]:
  207. cix_element = createCixVariable(cix_element, name, vartype="Object")
  208. method_name = sp[-1]
  209. cix_function = createCixFunction(cix_element, method_name)
  210. setCixDoc(cix_function, getSubElementText(p_tag), parse=True)
  211. if info.get("returnType"):
  212. addCixReturns(cix_function, info.get("returnType"))
  213. if info.get("signature"):
  214. setCixSignature(cix_function, info.get("signature"))
  215. else:
  216. setCixSignature(cix_function, signature)
  217. for arg_name, arg_type in info.get("args", []):
  218. addCixArgument(cix_function, arg_name, arg_type)
  219. def getPrototypeDocsFromWebpage():
  220. urlOpener = urllib.urlopen("http://www.sergiopereira.com/articles/prototype.js.html")
  221. return urlOpener.read()
  222. #return file("prototype.js.html").read()
  223. def updateCix(filename, content, updatePerforce=False):
  224. if updatePerforce:
  225. print os.popen("p4 edit %s" % (filename)).read()
  226. file(filename, "w").write(content)
  227. if updatePerforce:
  228. diff = os.popen("p4 diff %s" % (filename)).read()
  229. if len(diff.splitlines()) <= 1 and diff.find("not opened on this client") < 0:
  230. print "No change, reverting: %s" % os.popen("p4 revert %s" % (filename)).read()
  231. # Soup parsing of API documentation from webpage
  232. def main(cix_filename, updatePerforce=False):
  233. data = getPrototypeDocsFromWebpage()
  234. soup = BeautifulSoup(data)
  235. cix_root = createCixRoot(name="Prototype", description="JavaScript framework for web development")
  236. cix_file = createCixFile(cix_root, "prototype", lang="JavaScript")
  237. cix_module = createCixModule(cix_file, "prototype", lang="JavaScript")
  238. h4_tags = soup.html.body.div.findAll("h4")
  239. for h4_tag in h4_tags:
  240. processH4Tag(cix_module, h4_tag)
  241. ref_tag = soup.html.body.div.findAll(attrs={'name':"Reference"}, limit=1)[0]
  242. #print ref_tag
  243. processRefTags(cix_module, ref_tag)
  244. # Write out the tree
  245. updateCix(cix_filename, get_cix_string(cix_root), updatePerforce)
  246. if __name__ == '__main__':
  247. parser = OptionParser()
  248. parser.add_option("-u", "--update", dest="update_perforce",
  249. action="store_true", help="edit perforce cix for this file")
  250. (opts, args) = parser.parse_args()
  251. cix_filename = "prototype.cix"
  252. if opts.update_perforce:
  253. scriptpath = os.path.dirname(sys.argv[0])
  254. if not scriptpath:
  255. scriptpath = "."
  256. scriptpath = os.path.abspath(scriptpath)
  257. cix_directory = scriptpath
  258. # Get main codeintel directory
  259. for i in range(4):
  260. cix_directory = os.path.dirname(cix_directory)
  261. cix_filename = os.path.join(cix_directory, "lib", "codeintel2", "catalogs", cix_filename)
  262. main(cix_filename, opts.update_perforce)