[gnome-builder] gjs-symbols: Implement code indexer and various improvements
- From: Patrick Griffis <pgriffis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] gjs-symbols: Implement code indexer and various improvements
- Date: Tue, 12 Sep 2017 17:13:50 +0000 (UTC)
commit e601b5eba12aa1e65f850caaf44c5faab53b6cd0
Author: Patrick Griffis <tingping tingping se>
Date: Mon Sep 11 19:52:40 2017 -0400
gjs-symbols: Implement code indexer and various improvements
plugins/gjs-symbols/gjs_symbols.plugin | 2 +
plugins/gjs-symbols/gjs_symbols.py | 137 +++++++++++++++++++++++++++++---
2 files changed, 126 insertions(+), 13 deletions(-)
---
diff --git a/plugins/gjs-symbols/gjs_symbols.plugin b/plugins/gjs-symbols/gjs_symbols.plugin
index e014098..7384d57 100644
--- a/plugins/gjs-symbols/gjs_symbols.plugin
+++ b/plugins/gjs-symbols/gjs_symbols.plugin
@@ -8,3 +8,5 @@ Copyright=Copyright © 2017 Patrick Griffis
Builtin=true
X-Symbol-Resolver-Languages=js
X-Symbol-Resolver-Languages-Priority=0
+X-Code-Indexer-Languages=js
+X-Code-Indexer-Languages-Priority=0
diff --git a/plugins/gjs-symbols/gjs_symbols.py b/plugins/gjs-symbols/gjs_symbols.py
index e631883..c6b6495 100644
--- a/plugins/gjs-symbols/gjs_symbols.py
+++ b/plugins/gjs-symbols/gjs_symbols.py
@@ -1,4 +1,5 @@
import gi
+import os
import json
import threading
@@ -38,11 +39,14 @@ class JsSymbolNode(Ide.SymbolNode):
def __len__(self):
return len(self.children)
+ def __bool__(self):
+ return True
+
def __getitem__(self, key):
return self.children[key]
def __iter__(self):
- return self.children
+ return iter(self.children)
def __repr__(self):
return '<JsSymbolNode {} ({})'.format(self.props.name, self.props.kind)
@@ -51,7 +55,8 @@ class JsSymbolNode(Ide.SymbolNode):
class JsSymbolTree(GObject.Object, Ide.SymbolTree):
def __init__(self, dict_, file_):
super().__init__()
- # with open('dump.json', 'w+') as f:
+ # output = file_.get_path().replace('/', '_') + '.json'
+ # with open(output, 'w+') as f:
# f.write(json.dumps(dict_, indent=3))
self.root_node = self._node_from_dict(dict_, file_)
@@ -66,7 +71,7 @@ class JsSymbolTree(GObject.Object, Ide.SymbolTree):
# FIXME: Recursion is bad in Python, I know..
type_ = dict_['type']
- if type_ == 'Program':
+ if type_ == 'Program': # Root node
children = JsSymbolTree._nodes_from_list(dict_['body'], file_)
return JsSymbolNode(children, line=line, col=col,
kind=Ide.SymbolKind.PACKAGE,
@@ -77,6 +82,16 @@ class JsSymbolTree(GObject.Object, Ide.SymbolTree):
kind=Ide.SymbolKind.FUNCTION,
name=dict_['id']['name'],
file=file_)
+ elif type_ == 'Property': # Used for legacy GObject classes
+ if dict_['value']['type'] != 'FunctionExpression':
+ return None
+ name = dict_['key']['name']
+ if name == '_init':
+ return None
+ return JsSymbolNode([], line=line, col=col,
+ kind=Ide.SymbolKind.METHOD,
+ name=name,
+ file=file_)
elif type_ == 'VariableDeclaration':
decs = []
for dec in dict_['declarations']:
@@ -88,6 +103,8 @@ class JsSymbolTree(GObject.Object, Ide.SymbolTree):
if JsSymbolTree._is_module_import(dec):
kind = Ide.SymbolKind.MODULE
+ # For now these just aren't useful
+ continue
elif JsSymbolTree._is_gobject_class(dec):
for arg in dec['init']['arguments']:
if arg['type'] == 'ClassExpression':
@@ -97,8 +114,19 @@ class JsSymbolTree(GObject.Object, Ide.SymbolTree):
children = JsSymbolTree._nodes_from_list(arg['body'], file_)
name = arg['id']['name']
break
- elif dict_.get('kind', None) == 'const':
- kind = Ide.SymbolKind.CONSTANT
+ elif JsSymbolTree._is_legacy_gobject_class(dec):
+ kind = Ide.SymbolKind.CLASS
+ try:
+ arg = dec['init']['arguments'][0]
+ except IndexError:
+ continue
+
+ if arg['type'] != 'ObjectExpression':
+ continue
+
+ children = JsSymbolTree._nodes_from_list(arg['properties'], file_)
+ # elif dict_.get('kind', None) == 'const':
+ # kind = Ide.SymbolKind.CONSTANT
decs.append(JsSymbolNode(children, line=line, col=col,
kind=kind, name=name, file=file_))
return decs
@@ -110,7 +138,7 @@ class JsSymbolTree(GObject.Object, Ide.SymbolTree):
file=file_)
elif type_ == 'ClassMethod':
name = dict_['name']['name']
- if name == 'constructed':
+ if name in ('constructed', '_init'):
return None
return JsSymbolNode([], line=line, col=col,
kind=Ide.SymbolKind.METHOD,
@@ -123,8 +151,11 @@ class JsSymbolTree(GObject.Object, Ide.SymbolTree):
def _is_module_import(dict_):
try:
return dict_['init']['object']['name'] == 'imports'
- except KeyError:
- return False
+ except (KeyError, TypeError):
+ try:
+ return dict_['init']['object']['object']['name'] == 'imports'
+ except (KeyError, TypeError):
+ return False
@staticmethod
def _is_gobject_class(dict_):
@@ -134,6 +165,15 @@ class JsSymbolTree(GObject.Object, Ide.SymbolTree):
except KeyError:
return False
+ @staticmethod
+ def _is_legacy_gobject_class(dict_):
+ try:
+ callee = dict_['init']['callee']
+ return callee['object']['name'].lower() in ('gobject', 'lang') and callee['property']['name'] ==
'Class'
+ except KeyError:
+ return False
+
+ @staticmethod
def _nodes_from_list(list_, file_):
nodes = []
for i in list_:
@@ -168,9 +208,8 @@ class GjsSymbolProvider(Ide.Object, Ide.SymbolResolver):
def __init__(self):
super().__init__()
- def _get_launcher(self, file_):
- context = self.get_context()
-
+ @staticmethod
+ def _get_launcher(context, file_):
file_path = file_.get_path()
script = JS_SCRIPT %file_path
unsaved_file = context.get_unsaved_files().get_unsaved_file(file_)
@@ -179,7 +218,7 @@ class GjsSymbolProvider(Ide.Object, Ide.SymbolResolver):
runtime = pipeline.get_configuration().get_runtime()
launcher = runtime.create_launcher()
- launcher.set_flags(Gio.SubprocessFlags.STDIN_PIPE | Gio.SubprocessFlags.STDOUT_PIPE)
+ launcher.set_flags(Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_SILENCE)
launcher.push_args(('gjs', '-c', script))
if unsaved_file is not None:
launcher.push_argv(unsaved_file.get_content().get_data().decode('utf-8'))
@@ -197,7 +236,7 @@ class GjsSymbolProvider(Ide.Object, Ide.SymbolResolver):
def do_get_symbol_tree_async(self, file_, buffer_, cancellable, callback, user_data=None):
task = Gio.Task.new(self, cancellable, callback)
- launcher = self._get_launcher(file_)
+ launcher = self._get_launcher(self.get_context(), file_)
threading.Thread(target=self._get_tree_thread, args=(task, launcher, file_),
name='gjs-symbols-thread').start()
@@ -246,3 +285,75 @@ class GjsSymbolProvider(Ide.Object, Ide.SymbolResolver):
return None
+class JsCodeIndexEntries(GObject.Object, Ide.CodeIndexEntries):
+ def __init__(self, entries):
+ super().__init__()
+ self.entries = entries
+ self.entry_iter = None
+
+ def do_get_next_entry(self):
+ if self.entry_iter is None:
+ self.entry_iter = iter(self.entries)
+ try:
+ return next(self.entry_iter)
+ except StopIteration:
+ self.entry_iter = None
+ return None
+
+
+class GjsCodeIndexer(Ide.Object, Ide.CodeIndexer):
+ def __init__(self):
+ super().__init__()
+
+ @staticmethod
+ def _get_node_name(node):
+ prefix = {
+ Ide.SymbolKind.FUNCTION: 'f',
+ Ide.SymbolKind.METHOD: 'f',
+ Ide.SymbolKind.VARIABLE: 'v',
+ Ide.SymbolKind.CONSTANT: 'v',
+ Ide.SymbolKind.CLASS: 'c',
+ }.get(node.props.kind, 'x')
+ return prefix + '\x1F' + node.props.name
+
+ def do_index_file(self, file_, build_flags, cancellable):
+ if 'node_modules' in file_.get_path().split(os.sep):
+ return None # Avoid indexing these
+
+ launcher = GjsSymbolProvider._get_launcher(self.get_context(), file_)
+ proc = launcher.spawn()
+ success, stdout, stderr = proc.communicate_utf8(None, None)
+
+ if not success:
+ return None
+
+ ide_file = Ide.File(file=file_, context=self.get_context())
+ try:
+ root_node = JsSymbolTree._node_from_dict(json.loads(stdout), ide_file)
+ except (json.JSONDecodeError, UnicodeDecodeError) as e:
+ raise GLib.Error('Failed to decode gjs json: {}'.format(e))
+ except (IndexError, KeyError) as e:
+ raise GLib.Error('Failed to extract information from ast: {}'.format(e))
+
+ entries = []
+ # TODO: Avoid recreating the same data
+ for node in root_node:
+ entry = Ide.CodeIndexEntry(
+ key=node.props.file.get_path() + '|' + node.props.name, # Some unique value..
+ name=self._get_node_name(node),
+ kind=node.props.kind,
+ flags=node.props.flags,
+ begin_line=node.props.line,
+ begin_line_offset=node.props.col,
+ )
+ entries.append(entry)
+ return JsCodeIndexEntries(entries)
+
+ def do_generate_key_async(self, location, cancellable, callback, user_data=None):
+ # print('generate key')
+ task = Gio.Task.new(self, cancellable, callback)
+ task.return_boolean(False)
+
+ def do_generate_key_finish(self, result):
+ if result.propagate_boolean():
+ return ''
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]