[gnome-builder/wip/elad/jedi-gir-docs] jedi: show documentation next to completion suggestions
- From: Elad Alfassa <eladalfassa src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/elad/jedi-gir-docs] jedi: show documentation next to completion suggestions
- Date: Thu, 22 Oct 2015 14:53:00 +0000 (UTC)
commit e3ad3e6a443bd057e9264e0d761ff67770ff1f57
Author: Elad Alfassa <elad fedoraproject org>
Date: Thu Oct 22 17:52:45 2015 +0300
jedi: show documentation next to completion suggestions
plugins/jedi/jedi_plugin.py | 101 ++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 100 insertions(+), 1 deletions(-)
---
diff --git a/plugins/jedi/jedi_plugin.py b/plugins/jedi/jedi_plugin.py
index db51a18..6cdcac5 100644
--- a/plugins/jedi/jedi_plugin.py
+++ b/plugins/jedi/jedi_plugin.py
@@ -19,8 +19,14 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-
import gi
+# lxml is faster than the Python standard library xml, and can ignore invalid
+# characters (which occur in some .gir files).
+# gnome-code-assistance also needs lxml, so I think it's okay to use it here.
+import lxml.etree
+import os
+import os.path
+import sqlite3
gi.require_version('Gtk', '3.0')
gi.require_version('GtkSource', '3.0')
gi.require_version('Ide', '1.0')
@@ -32,6 +38,8 @@ from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import GtkSource
from gi.repository import Ide
+from gi.types import GObjectMeta
+from gi.types import StructMeta
gi_importer = DynamicImporter('gi.repository')
try:
@@ -132,6 +140,55 @@ except ImportError:
# relatively fast enough for interactivity.
import threading
+# Build documentation DB and ensure it's up to date
+# This sits here and not in a special class because it needs to run at app startup.
+# Building the DB can slow down the startup process in a noticable way,
+# we could thread it, but that's a problem because sqlite is not thread safe,
+# and if someone starts typing while the DB is still building, they'd get no docs
+gir_path = '/usr/share/gir-1.0'
+ns = {'core': 'http://www.gtk.org/introspection/core/1.0',
+ 'c': 'http://www.gtk.org/introspection/c/1.0'}
+doc_db_path = os.path.join(GLib.get_user_data_dir(), 'gnome-builder', 'doc.db')
+db = sqlite3.connect(doc_db_path)
+cursor = db.cursor()
+cursor.execute('CREATE TABLE IF NOT EXISTS doc (symbol text, library_version text, doc text, gir_file text)')
+cursor.execute('CREATE TABLE IF NOT EXISTS girfiles (file text, last_modified integer)')
+
+# I would use scandir for better performance, but it requires newer Python
+for gir_file in os.listdir(gir_path):
+ filename = os.path.join(gir_path, gir_file)
+ mtime = os.stat(filename).st_mtime
+ cursor.execute('SELECT * from girfiles WHERE file=?', (filename,))
+ result = cursor.fetchone()
+ if result is None:
+ cursor.execute('INSERT INTO girfiles VALUES (?, ?)', (filename, mtime))
+ else:
+ if result[1] >= mtime:
+ continue
+ else:
+ # updated
+ cursor.execute('DELETE FROM doc WHERE gir_file=?', (filename,))
+ cursor.execute('UPDATE girfiles SET last_modified=? WHERE file=?', (mtime, filename))
+ parser = lxml.etree.XMLParser(recover=True)
+ tree = lxml.etree.parse(filename, parser=parser)
+ namespace = tree.find('core:namespace', namespaces=ns)
+ library_version = namespace.attrib['version']
+ for node in namespace.findall('core:class', namespaces=ns):
+ doc = node.find('core:doc', namespaces=ns)
+ if doc is not None:
+ db.execute("INSERT INTO doc VALUES (?, ?, ?, ?)",
+ (node.attrib['{http://www.gtk.org/introspection/glib/1.0}type-name'],
library_version, doc.text, filename))
+ for method in namespace.findall('core:method', namespaces=ns) + \
+ namespace.findall('core:constructor', namespaces=ns) + \
+ namespace.findall('core:function', namespaces=ns):
+ doc = method.find('core:doc', namespaces=ns)
+ if doc is not None:
+ db.execute("INSERT INTO doc VALUES (?, ?, ?, ?)",
+ (method.attrib['{http://www.gtk.org/introspection/c/1.0}identifier'],
library_version, doc.text, filename))
+cursor.close()
+db.commit()
+db.close()
+
class GIParam(object):
"A pygobject ArgInfo wrapper to make it similar to Jedi's Param class"
@@ -447,9 +504,51 @@ class JediCompletionProposal(Ide.CompletionItem, GtkSource.CompletionProposal):
def do_changed(self):
pass
+ def do_get_info(self):
+ if hasattr(self.completion._definition, 'obj'):
+ obj = self.completion._definition.obj
+ else:
+ return self.completion.docstring()
+ symbol = None
+ namespace = None
+
+ if type(obj) == GObjectMeta or type(obj) == StructMeta:
+ if hasattr(obj, '__info__'):
+ symbol = obj.__info__.get_type_name()
+ namespace = obj.__info__.get_namespace()
+ elif type(obj) == FunctionInfo:
+ symbol = obj.get_symbol()
+ namespace = obj.get_namespace()
+
+ if symbol is not None:
+ # we need to walk down the path to find the module so we can get the version
+ # TODO find a more efficient way to do this.
+ parent = self.completion._definition.parent
+ found = False
+ while not found:
+ new_parent = parent.parent
+ if new_parent is None:
+ found = True
+ else:
+ parent = new_parent
+ version = parent.obj._version
+ print(version)
+ db = sqlite3.connect(doc_db_path)
+ cursor = db.cursor()
+ cursor.execute('SELECT doc FROM doc WHERE symbol=? AND library_version=?', (symbol, version))
+ result = cursor.fetchone()
+ cursor.close()
+ db.close()
+ if result is not None:
+ return result[0]
+
+ return self.completion.docstring()
+
+
def is_completable_char(ch):
return ch in ('_', '.') or ch.isalnum()
+
def get_param_description(param):
if hasattr(param, 'description'):
return param.description.replace('\n', '')
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]