diff --git a/fakegir.py b/fakegir.py index 2992a4d..18e0f0e 100644 --- a/fakegir.py +++ b/fakegir.py @@ -8,6 +8,38 @@ FAKEGIR_PATH = os.path.join(os.path.expanduser('~'), '.cache/fakegir') XMLNS = "http://www.gtk.org/introspection/core/1.0" +GIR_TO_NATIVE_TYPEMAP = {'gboolean': 'bool', + 'gint': 'int', + 'guint': 'int', + 'gint64': 'int', + 'guint64': 'int', + 'none': 'None', + 'gchar': 'str', + 'guchar': 'str', + 'gchar*': 'str', + 'guchar*': 'str', + 'glong': 'long', + 'gulong': 'long', + 'glong64': 'long', + 'gulong64': 'long', + 'gfloat': 'float', + 'gdouble': 'float', + 'string': 'str', + 'utf8': 'unicode', + 'GString': 'str'} + + +def get_native_type(typename): + if typename[:len("const ")] == "const ": + typename = typename.replace("const ", "") + + if (typename in GIR_TO_NATIVE_TYPEMAP): + return GIR_TO_NATIVE_TYPEMAP[typename] + else: + return typename + + return "" + def get_docstring(callable_tag): """Return docstring text for a callable""" @@ -17,6 +49,7 @@ def get_docstring(callable_tag): return element.text.replace("\\x", 'x').encode('utf-8') + b"\n" return '' + def get_parameter_type(element): """Returns the type of a parameter""" parm_type = "" @@ -29,6 +62,18 @@ def get_parameter_type(element): return parm_type +def get_parameter_doc(element): + """Returns the doc of a parameter""" + parm_doc = "" + for elem_property in element: + tag = etree.QName(elem_property) + if tag.localname == "doc": + parm_doc = element.text.replace("\\x", 'x').encode('utf-8').replace("\n", " ").strip() + break + + return parm_doc + + def get_parameters(element): """Return the parameters of a callable""" params = [] @@ -44,43 +89,78 @@ def get_parameters(element): param_name = param.attrib['name'] parm_type = get_parameter_type(param) + parm_doc = get_docstring(param).replace("\n", " ").strip() if keyword.iskeyword(param_name): param_name = "_" + param_name if not param_name in params: - params.append( (param_name, parm_type) ) + params.append((param_name, parm_doc, parm_type)) except KeyError: pass return params +def get_returntype(element): + """Return the return-type of a callable""" + for elem_property in element: + tag = etree.QName(elem_property) + if tag.localname == 'return-value': + return_doc = get_docstring(elem_property).replace("\n", " ").strip() + for subelem in elem_property: + try: + subtag = etree.QName(subelem) + if subtag.localname == "type": + return (return_doc, subelem.attrib['name']) + + except KeyError: + pass + return ("", "none") -def insert_function(name, args, depth, docstring=''): + +def insert_function(name, args, returntype, depth, docstring='', annotation=''): """Returns a function as a string""" if keyword.iskeyword(name): name = "_" + name arglist = ", ".join([arg[0] for arg in args]) - epydoc_str = "\n".join( - ["@param %s: %s" % (pname, ptype) if pname != "self" else "" - for (pname, ptype) in args]) + epydoc_doc_strs = ["@param %s: %s" % (pname, pdoc) + if (len(pdoc) > 0 and pname != "self") else "" + for (pname, pdoc, ptype) in args] + + epydoc_type_strs = ["@type %s: %s" % (pname, get_native_type(ptype)) + if (len(ptype) > 0 and pname != "self") else "" + for (pname, pdoc, ptype) in args] + return_docstrs = [] + if (returntype[1] == 'none'): + return_docstrs = ["@rtype: None"] + else: + return_docstrs = ["@returns: %s" % returntype[0], + "@rtype: %s" % get_native_type(returntype[1])] + + def do_indent(lines): + return [' '*(depth+1) + l for l in lines] + + from itertools import chain full_docstr = "\n".join( - [' '*(depth+1) + l - for l in (docstring + - "\n" + - epydoc_str + - "\n").split("\n")]) - return "%sdef %s(%s):\n%s\"\"\"\n%s\"\"\"\n" % (' ' * depth, - name, - arglist, - ' ' * (depth + 1), - full_docstr) + do_indent(chain(docstring.split("\n"), + filter(lambda s: len(s) > 0, epydoc_doc_strs), + filter(lambda s: len(s) > 0, epydoc_type_strs), + return_docstrs, + [""], + ))) + + return "%s\n%sdef %s(%s):\n%s\"\"\"\n%s\"\"\"\n" % (' '*depth + annotation if len(annotation) > 0 else "", + ' ' * depth, + name, + arglist, + ' ' * (depth + 1), + full_docstr) def insert_enum(element): - """Returns an enum (class with attributes only) as text""" + """returns an enum (class with attributes only) as text""" enum_name = element.attrib['name'] docstring = get_docstring(element) enum_content = "class %s:\n \"\"\"%s\"\"\"\n" % (enum_name, docstring) @@ -96,21 +176,47 @@ def insert_enum(element): def extract_methods(class_tag): - """Return methods from a class element""" + """return methods from a class element""" + methods_content = '' + for element in class_tag: + tag = etree.QName(element) + if (tag.localname == 'method') or (tag.localname == 'virtual-method'): + method_name = element.attrib['name'] + docstring = get_docstring(element) + params = get_parameters(element) + returntype = get_returntype(element) + + methods_content += insert_function(method_name, params, returntype, + 1, docstring) + + return methods_content + + +def extract_constructors(class_tag): + """return the constructor methods for this class""" + class_name = class_tag.attrib["name"] methods_content = '' for element in class_tag: tag = etree.QName(element) - if tag.localname == 'method': + if (tag.localname == 'constructor'): method_name = element.attrib['name'] docstring = get_docstring(element) params = get_parameters(element) - methods_content += insert_function(method_name, params, 1, - docstring) + returntype = ("Newly created " + class_name, class_name) + + if method_name == "new": + params_init = list(params) + params_init.insert(0, ("self", "", "")) + methods_content += insert_function("__init__", params_init, + returntype, 1, docstring) + + methods_content += insert_function(method_name, params, + returntype, 1, docstring, annotation="@staticmethod") return methods_content def build_classes(classes): - """Order classes with correct dependency order also return external + """order classes with correct dependency order also return external imports""" classes_text = "" imports = set() @@ -141,7 +247,7 @@ def build_classes(classes): def extract_namespace(namespace): - """Extract all information from a gir namespace""" + """extract all information from a gir namespace""" namespace_content = "" classes = [] for element in namespace: @@ -159,6 +265,7 @@ def extract_namespace(namespace): parents.append(implement.attrib['name']) class_content = ("\nclass %s(%s):\n \"\"\"%s\"\"\"\n" % (class_name, ", ".join(parents), docstring)) + class_content += extract_constructors(element) class_content += extract_methods(element) classes.append((class_name, parents, class_content)) if (tag_name == 'enumeration') or (tag_name == "bitfield"): @@ -167,7 +274,8 @@ def extract_namespace(namespace): function_name = element.attrib['name'] docstring = get_docstring(element) params = get_parameters(element) - namespace_content += insert_function(function_name, params, 0, + returntype = get_returntype(element) + namespace_content += insert_function(function_name, params, returntype, 0, docstring) if tag_name == 'constant': constant_name = element.attrib['name'] diff --git a/force_cache.sh b/force_cache.sh new file mode 100755 index 0000000..9eb9e8a --- /dev/null +++ b/force_cache.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +JEDI_PATH=$HOME/.vim/bundle/YouCompleteMe/third_party/jedi + +#python fakegir.py +rm -rf $HOME/.cache/jedi + +pkgs="" +echo '#/bin/env python' >/tmp/fakeprg.py +for f in ~/.cache/fakegir/gi/repository/*.py; do + pkgname=`basename $f|cut -d . -f 1` + pkgs="$pkgs $pkgname" + echo "from gi.repository import $pkgname" >> /tmp/fakeprg.py +done + + +for pkgname in $pkgs; do + echo Forcing cache for $pkgname + cp /tmp/fakeprg.py /tmp/${pkgname}-fakeprg.py + compl="w = ${pkgname}." + echo "$compl" >> /tmp/${pkgname}-fakeprg.py + fakeprg_lines=`wc -l /tmp/${pkgname}-fakeprg.py| cut -d ' ' -f 1` + compl_col=${#compl} + + export PYTHONPATH=$HOME/.cache/fakegir + pushd $JEDI_PATH + $JEDI_PATH/sith.py -f run completions /tmp/${pkgname}-fakeprg.py $fakeprg_lines $compl_col +done