/Packages/SublimeCodeIntel/libs/codeintel2/gencix_utils.py
Python | 364 lines | 316 code | 4 blank | 44 comment | 1 complexity | 91f3a532d5c6548c17c3a47be1f2cc3a 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"""Shared CIX tools for Code Intelligence
39
40 CIX helpers for codeintel creation. Code Intelligence XML format. See:
41 http://specs.tl.activestate.com/kd/kd-0100.html#xml-based-import-export-syntax-cix
42"""
43
44import os
45import sys
46import re
47import shutil
48from cStringIO import StringIO
49import warnings
50
51from ciElementTree import Element, ElementTree, SubElement
52from codeintel2.util import parseDocSummary
53
54# Dictionary of known js types and what they map to
55known_javascript_types = {
56 "object": "Object",
57 "obj": "Object",
58 "function": "Function",
59 "array": "Array",
60 "string": "String",
61 "text": "String",
62 "int": "Number",
63 "integer": "Number",
64 "number": "Number",
65 "numeric": "Number",
66 "decimal": "Number",
67 "short": "Number",
68 "unsigned short": "Number",
69 "long": "Number",
70 "unsigned long":"Number",
71 "float": "Number",
72 "bool": "Boolean",
73 "boolean": "Boolean",
74 "true": "Boolean",
75 "false": "Boolean",
76 "date": "Date",
77 "regexp": "RegExp",
78 # Dom elements
79 "element": "Element",
80 "node": "Node",
81 "domnode": "DOMNode",
82 "domstring": "DOMString",
83 "widget": "Widget",
84 "domwidget": "DOMWidget",
85 "htmlelement": "HTMLElement",
86 "xmldocument": "XMLDocument",
87 "htmldocument": "HTMLDocument",
88 # Special
89 "xmlhttprequest": "XMLHttpRequest",
90 "void": "",
91 # Mozilla special
92 "UTF8String": "String",
93 "AString": "String",
94}
95
96def standardizeJSType(vartype):
97 """Return a standardized name for the given type if it is a known type.
98
99 Example1: given vartype of "int", returns "Number"
100 Example2: given vartype of "YAHOO.tool", returns "YAHOO.tool"
101 """
102
103 if vartype:
104 typename = known_javascript_types.get(vartype.lower(), None)
105 if typename is None:
106 #print "Unknown type: %s" % (vartype)
107 return vartype
108 return typename
109
110spacere = re.compile(r'\s+')
111def condenseSpaces(s):
112 """Remove any line enedings and condense multiple spaces"""
113
114 s = s.replace("\n", " ")
115 s = spacere.sub(' ', s)
116 return s.strip()
117
118def remove_directory(dirpath):
119 """ Recursively remove the directory path given """
120
121 if os.path.exists(dirpath):
122 shutil.rmtree(dirpath, ignore_errors=True)
123
124def getText(elem):
125 """Return the internal text for the given ElementTree node"""
126
127 l = []
128 for element in elem.getiterator():
129 if element.text:
130 l.append(element.text)
131 if element.tail:
132 l.append(element.tail)
133 return " ".join(l)
134
135def getAllTextFromSubElements(elem, subelementname):
136 descnodes = elem.findall(subelementname)
137 if len(descnodes) == 1:
138 return getText(descnodes[0])
139 return None
140
141_invalid_char_re = re.compile(u'[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD]')
142def strip_invalid_xml_chars(s):
143 """Return the string with any invalid XML characters removed.
144
145 The valid characters are listed here:
146 http://www.w3.org/TR/REC-xml/#charsets
147 #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
148 """
149 return _invalid_char_re.sub("", s)
150
151def setCixDoc(cixelement, doctext, parse=False):
152 if parse:
153 doclines = parseDocSummary(doctext.splitlines(0))
154 doctext = "\n".join(doclines)
155 elif sys.platform.startswith("win"):
156 doctext = doctext.replace("\r\n", "\n")
157 #TODO: By default clip doc content down to a smaller set -- just
158 # enough for a good calltip. By then also want an option to
159 # *not* clip, for use in documentation generation.
160 #if len(doctext) > 1000:
161 # warnings.warn("doctext for cixelement: %r has length: %d" % (
162 # cixelement.get("name"), len(doctext)))
163 cixelement.attrib["doc"] = strip_invalid_xml_chars(doctext)
164
165def setCixDocFromNodeChildren(cixelement, node, childnodename):
166 doctext = getAllTextFromSubElements(node, childnodename)
167 if doctext:
168 setCixDoc(cixelement, condenseSpaces(doctext), parse=True)
169
170def addCixArgument(cixelement, argname, argtype=None, doc=None):
171 cixarg = SubElement(cixelement, "variable", ilk="argument", name=argname)
172 if argtype:
173 addCixType(cixarg, argtype)
174 if doc:
175 setCixDoc(cixarg, doc)
176 return cixarg
177
178def addCixReturns(cixelement, returntype=None):
179 if returntype and returntype != "void":
180 cixelement.attrib["returns"] = returntype
181
182def addCixType(cixobject, vartype):
183 if vartype:
184 cixobject.attrib["citdl"] = vartype
185
186def addCixAttribute(cixobject, attribute):
187 attrs = cixobject.get("attributes")
188 if attrs:
189 sp = attrs.split()
190 if attribute not in sp:
191 attrs = "%s %s" % (attrs, attribute)
192 else:
193 attrs = attribute
194 cixobject.attrib["attributes"] = attrs
195
196def addClassRef(cixclass, name):
197 refs = cixclass.get("classrefs", None)
198 if refs:
199 if name not in refs.split(" "):
200 cixclass.attrib["classrefs"] = "%s %s" % (refs, name)
201 else:
202 cixclass.attrib["classrefs"] = "%s" % (name)
203
204def addInterfaceRef(cixinterface, name):
205 refs = cixinterface.get("interfacerefs", None)
206 if refs:
207 if name not in refs.split(" "):
208 cixinterface.attrib["interfacerefs"] = "%s %s" % (refs, name)
209 else:
210 cixinterface.attrib["interfacerefs"] = "%s" % (name)
211
212def setCixSignature(cixelement, signature):
213 cixelement.attrib["signature"] = signature
214
215def createCixVariable(cixobject, name, vartype=None, attributes=None):
216 if attributes:
217 v = SubElement(cixobject, "variable", name=name,
218 attributes=attributes)
219 else:
220 v = SubElement(cixobject, "variable", name=name)
221 if vartype:
222 addCixType(v, vartype)
223 return v
224
225def createCixFunction(cixmodule, name, attributes=None):
226 if attributes:
227 return SubElement(cixmodule, "scope", ilk="function", name=name,
228 attributes=attributes)
229 else:
230 return SubElement(cixmodule, "scope", ilk="function", name=name)
231
232def createCixInterface(cixmodule, name):
233 return SubElement(cixmodule, "scope", ilk="interface", name=name)
234
235def createCixClass(cixmodule, name):
236 return SubElement(cixmodule, "scope", ilk="class", name=name)
237
238def createCixNamespace(cixmodule, name):
239 return SubElement(cixmodule, "scope", ilk="namespace", name=name)
240
241def createCixModule(cixfile, name, lang, src=None):
242 if src is None:
243 return SubElement(cixfile, "scope", ilk="blob", name=name, lang=lang)
244 else:
245 return SubElement(cixfile, "scope", ilk="blob", name=name, lang=lang, src=src)
246
247def createOrFindCixModule(cixfile, name, lang, src=None):
248 for module in cixfile.findall("./scope"):
249 if module.get("ilk") == "blob" and module.get("name") == name and \
250 module.get("lang") == lang:
251 return module
252 return createCixModule(cixfile, name, lang, src)
253
254def createCixFile(cix, path, lang="JavaScript", mtime="1102379523"):
255 return SubElement(cix, "file",
256 lang=lang,
257 #mtime=mtime,
258 path=path)
259
260def createCixRoot(version="2.0", name=None, description=None):
261 cixroot = Element("codeintel", version=version)
262 if name is not None:
263 cixroot.attrib["name"] = name
264 if description is not None:
265 cixroot.attrib["description"] = description
266 return cixroot
267
268# Add .text and .tail values to make the CIX output pretty. (Only have
269# to avoid "doc" tags: they are the only ones with text content.)
270def prettify(elem, level=0, indent=' ', youngestsibling=0):
271 if elem and elem.tag != "doc":
272 elem.text = '\n' + (indent*(level+1))
273 for i in range(len(elem)):
274 prettify(elem[i], level+1, indent, i==len(elem)-1)
275 elem.tail = '\n' + (indent*(level-youngestsibling))
276
277def get_cix_string(cix, prettyFormat=True):
278 # Get the CIX.
279 if prettyFormat:
280 prettify(cix)
281 cixstream = StringIO()
282 cixtree = ElementTree(cix)
283 cixstream.write('<?xml version="1.0" encoding="UTF-8"?>\n')
284 cixtree.write(cixstream)
285 cixcontent = cixstream.getvalue()
286 cixstream.close()
287 return cixcontent
288
289def outline_ci_elem(elem, _lvl=0, brief=False, doSort=False, includeLineNos=False):
290 """Return an outline of the given codeintel tree element."""
291 indent = ' '
292 result = []
293
294 def _dump(s):
295 if includeLineNos:
296 startline = elem.get("line")
297 lineend = elem.get("lineend")
298 line_str = ""
299 if startline or lineend:
300 line_str = " (%r-%r)" % (startline, lineend)
301 result.append(indent*_lvl + s + line_str + '\n')
302 else:
303 result.append(indent*_lvl + s + '\n')
304
305 if elem.tag == "codeintel":
306 _lvl -= 1 # don't count this one
307 elif brief:
308 name = elem.get("name")
309 if name:
310 _dump(name)
311 elif elem.tag == "file":
312 lang = elem.get("lang")
313 _dump("file %(path)s [%(lang)s]" % elem.attrib)
314 elif elem.tag == "variable":
315 if elem.get("ilk") == "argument":
316 s = "arg "+elem.get("name") # skip?
317 else:
318 s = "var "+elem.get("name")
319 if elem.get("citdl"):
320 s += " [%s]" % elem.get("citdl")
321 _dump(s)
322 elif elem.tag == "scope" and elem.get("ilk") == "function" \
323 and elem.get("signature"):
324 _dump("function %s" % elem.get("signature").split('\n')[0])
325 elif elem.tag == "scope" and elem.get("ilk") == "blob":
326 lang = elem.get("lang")
327 _dump("blob %(name)s [%(lang)s]" % elem.attrib)
328 elif elem.tag == "scope" and elem.get("ilk") == "class" \
329 and elem.get("classrefs"):
330 _dump("%s %s(%s)" % (elem.get("ilk"), elem.get("name"),
331 ', '.join(elem.get("classrefs").split())))
332 elif elem.tag == "scope":
333 _dump("%s %s" % (elem.get("ilk"), elem.get("name")))
334 elif elem.tag == "import":
335 module = elem.get("module")
336 symbol = elem.get("symbol")
337 alias = elem.get("alias")
338 value = "import '%s" % (module, )
339 if symbol:
340 value += ".%s" % (symbol, )
341 value += "'"
342 if alias:
343 value +" as %r" % (alias, )
344 _dump(value)
345 else:
346 raise ValueError("unknown tag: %r (%r)" % (elem.tag, elem))
347
348 if doSort and hasattr(elem, "names") and elem.names:
349 for name in sorted(elem.names.keys()):
350 child = elem.names[name]
351 result.append(outline_ci_elem(child, _lvl=_lvl+1,
352 brief=brief, doSort=doSort,
353 includeLineNos=includeLineNos))
354 else:
355 for child in elem:
356 result.append(outline_ci_elem(child, _lvl=_lvl+1,
357 brief=brief, doSort=doSort,
358 includeLineNos=includeLineNos))
359 return "".join(result)
360
361def remove_cix_line_numbers_from_tree(tree):
362 for node in tree.getiterator():
363 node.attrib.pop("line", None)
364 node.attrib.pop("lineend", None)