| Index: runtime/observatory/lib/src/elements/class_tree.dart
|
| diff --git a/runtime/observatory/lib/src/elements/class_tree.dart b/runtime/observatory/lib/src/elements/class_tree.dart
|
| index 16f6928fbd72a7b193f0445f2b021e5909b929b9..f17f27b7d3213d495d5aed1d4e43bdf5a0b0fd21 100644
|
| --- a/runtime/observatory/lib/src/elements/class_tree.dart
|
| +++ b/runtime/observatory/lib/src/elements/class_tree.dart
|
| @@ -4,150 +4,182 @@
|
|
|
| library class_tree_element;
|
|
|
| -import 'observatory_element.dart';
|
| -import 'dart:async';
|
| import 'dart:html';
|
| -import 'package:logging/logging.dart';
|
| -import 'package:observatory/app.dart';
|
| -import 'package:observatory/service.dart';
|
| -import 'package:polymer/polymer.dart';
|
| -
|
| -class ClassTreeRow extends TableTreeRow {
|
| - @reflectable final Isolate isolate;
|
| - @reflectable final Class cls;
|
| - ClassTreeRow(this.isolate, this.cls, TableTree tree, ClassTreeRow parent)
|
| - : super(tree, parent) {
|
| +import 'dart:async';
|
| +import 'package:observatory/models.dart' as M;
|
| +import 'package:observatory/src/elements/class_ref.dart';
|
| +import 'package:observatory/src/elements/containers/virtual_tree.dart';
|
| +import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
| +import 'package:observatory/src/elements/helpers/tag.dart';
|
| +import 'package:observatory/src/elements/helpers/uris.dart';
|
| +import 'package:observatory/src/elements/nav/bar.dart';
|
| +import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
| +import 'package:observatory/src/elements/nav/menu.dart';
|
| +import 'package:observatory/src/elements/nav/notify.dart';
|
| +import 'package:observatory/src/elements/nav/top_menu.dart';
|
| +import 'package:observatory/src/elements/nav/vm_menu.dart';
|
| +
|
| +
|
| +class ClassTreeElement extends HtmlElement implements Renderable{
|
| + static const tag = const Tag<ClassTreeElement>('class-tree',
|
| + dependencies: const [ClassRefElement.tag,
|
| + NavBarElement.tag,
|
| + NavIsolateMenuElement.tag,
|
| + NavMenuElement.tag,
|
| + NavNotifyElement.tag,
|
| + NavTopMenuElement.tag,
|
| + NavVMMenuElement.tag,
|
| + VirtualTreeElement.tag]);
|
| +
|
| + RenderingScheduler _r;
|
| +
|
| + Stream<RenderedEvent<ClassTreeElement>> get onRendered => _r.onRendered;
|
| +
|
| + M.VMRef _vm;
|
| + M.IsolateRef _isolate;
|
| + M.EventRepository _events;
|
| + M.NotificationRepository _notifications;
|
| + M.ClassRepository _classes;
|
| + M.Class _object;
|
| + final _subclasses = <String, Iterable<M.Class>>{};
|
| + final _mixins = <String, List<M.Instance>>{};
|
| +
|
| + factory ClassTreeElement(M.VMRef vm, M.IsolateRef isolate,
|
| + M.EventRepository events,
|
| + M.NotificationRepository notifications,
|
| + M.ClassRepository classes,
|
| + {RenderingQueue queue}) {
|
| + assert(vm != null);
|
| assert(isolate != null);
|
| - assert(cls != null);
|
| + assert(events != null);
|
| + assert(notifications != null);
|
| + assert(classes != null);
|
| + ClassTreeElement e = document.createElement(tag.name);
|
| + e._r = new RenderingScheduler(e, queue: queue);
|
| + e._vm = vm;
|
| + e._isolate = isolate;
|
| + e._events = events;
|
| + e._notifications = notifications;
|
| + e._classes = classes;
|
| + return e;
|
| }
|
|
|
| - void _addChildren(List<Class> subclasses) {
|
| - for (var subclass in subclasses) {
|
| - if (subclass.isPatch) {
|
| - continue;
|
| - }
|
| - if (subclass.mixin != null) {
|
| - _addChildren(subclass.subclasses);
|
| - } else {
|
| - var row = new ClassTreeRow(isolate, subclass, tree, this);
|
| - children.add(row);
|
| - }
|
| - }
|
| - }
|
| + ClassTreeElement.created() : super.created();
|
|
|
| - Future _addMixins(Class cls) async {
|
| - var classCell = flexColumns[0];
|
| - if (cls.superclass == null) {
|
| - return;
|
| - }
|
| - bool first = true;
|
| - while (cls.superclass != null && cls.superclass.mixin != null) {
|
| - cls = cls.superclass;
|
| - await cls.mixin.load();
|
| - var span = new SpanElement();
|
| - span.style.alignSelf = 'center';
|
| - span.style.whiteSpace = 'pre';
|
| - if (first) {
|
| - span.text = ' with ';
|
| - } else {
|
| - span.text = ', ';
|
| - }
|
| - classCell.children.add(span);
|
| - var mixinRef = new Element.tag('class-ref');
|
| - mixinRef.ref = cls.mixin.typeClass;
|
| - mixinRef.style.alignSelf = 'center';
|
| - classCell.children.add(mixinRef);
|
| - first = false;
|
| - }
|
| + @override
|
| + void attached() {
|
| + super.attached();
|
| + _refresh();
|
| + _r.enable();
|
| }
|
|
|
| - Future _addClass(Class cls) async {
|
| - var classCell = flexColumns[0];
|
| - classCell.style.justifyContent = 'flex-start';
|
| - var classRef = new Element.tag('class-ref');
|
| - classRef.ref = cls;
|
| - classRef.style.alignSelf = 'center';
|
| - classCell.children.add(classRef);
|
| - if (cls.superclass != null && cls.superclass.mixin != null) {
|
| - await _addMixins(cls);
|
| - }
|
| - if (cls.subclasses.isNotEmpty) {
|
| - var span = new SpanElement();
|
| - span.style.paddingLeft = '.5em';
|
| - span.style.alignSelf = 'center';
|
| - int subclassCount = _indirectSubclassCount(cls) - 1;
|
| - if (subclassCount > 1) {
|
| - span.text = '($subclassCount subclasses)';
|
| - } else {
|
| - span.text = '($subclassCount subclass)';
|
| - }
|
| - classCell.children.add(span);
|
| - }
|
| + @override
|
| + void detached() {
|
| + super.detached();
|
| + children = [];
|
| + _r.disable(notify: true);
|
| }
|
|
|
| - void onShow() {
|
| - super.onShow();
|
| - if (children.length == 0) {
|
| - _addChildren(cls.subclasses);
|
| - }
|
| - _addClass(cls);
|
| + VirtualTreeElement _tree;
|
| +
|
| + void render() {
|
| + children = [
|
| + new NavBarElement(queue: _r.queue)
|
| + ..children = [
|
| + new NavTopMenuElement(queue: _r.queue),
|
| + new NavVMMenuElement(_vm, _events, queue: _r.queue),
|
| + new NavIsolateMenuElement(_isolate, _events, queue: _r.queue),
|
| + new NavMenuElement('class hierarchy', link: Uris.classTree(_isolate),
|
| + last: true, queue: _r.queue),
|
| + new NavNotifyElement(_notifications, queue: _r.queue)
|
| + ],
|
| + new DivElement()
|
| + ..classes = ['content-centered']
|
| + ..children = [
|
| + new HeadingElement.h1()..text = 'Class Hierarchy',
|
| + new BRElement(), new HRElement(),
|
| + _object == null ? (new HeadingElement.h2()..text = 'Loading...')
|
| + : _createTree()
|
| + ]
|
| + ];
|
| }
|
|
|
| - static int _indirectSubclassCount(var cls) {
|
| - int count = 0;
|
| - if (cls.mixin == null) {
|
| - // Don't count synthetic mixin classes in subclass count.
|
| - count++;
|
| - }
|
| - for (var subclass in cls.subclasses) {
|
| - count += _indirectSubclassCount(subclass);
|
| - }
|
| - return count;
|
| + Element _createTree() {
|
| + _tree = new VirtualTreeElement(_create, _update, _children,
|
| + items: [_object], queue: _r.queue);
|
| + _tree.expand(_object, autoExpandSingleChildNodes: true);
|
| + return _tree;
|
| }
|
|
|
| - bool hasChildren() {
|
| - return cls.subclasses.isNotEmpty;
|
| + Future _refresh() async {
|
| + _object = null;
|
| + _subclasses.clear();
|
| + _mixins.clear();
|
| + _object = await _register(await _classes.getObject());
|
| + _r.dirty();
|
| }
|
| -}
|
| -
|
| -
|
| -@CustomTag('class-tree')
|
| -class ClassTreeElement extends ObservatoryElement {
|
| - @observable Isolate isolate;
|
|
|
| - TableTree tree;
|
| -
|
| - ClassTreeElement.created() : super.created();
|
| + Future<M.Class> _register(M.Class cls) async {
|
| + _subclasses[cls.id] = await Future.wait(
|
| + (await Future.wait(cls.subclasses.map(_getActualChildrens)))
|
| + .expand((f) => f)
|
| + .map(_register)
|
| + );
|
| + return cls;
|
| + }
|
|
|
| - @override
|
| - void attached() {
|
| - super.attached();
|
| - var tableBody = shadowRoot.querySelector('#tableTreeBody');
|
| - assert(tableBody != null);
|
| - tree = new TableTree(tableBody, 1);
|
| - if (isolate != null) {
|
| - _update(isolate.objectClass);
|
| + Future<Iterable<M.Class>> _getActualChildrens(M.ClassRef ref) async {
|
| + var cls = await _classes.get(ref.id);
|
| + if (cls.isPatch) {
|
| + return const [];
|
| + }
|
| + if (cls.mixin == null) {
|
| + return [cls];
|
| }
|
| + return (await Future.wait(cls.subclasses.map(_getActualChildrens)))
|
| + .expand((f) => f)
|
| + ..forEach((subcls) {
|
| + _mixins[subcls.id] = (_mixins[subcls.id] ?? [])..add(cls.mixin);
|
| + });
|
| }
|
|
|
| - isolateChanged(oldValue) {
|
| - isolate.getClassHierarchy().then((objectClass) {
|
| - _update(objectClass);
|
| - });
|
| + static Element _create(toggle) {
|
| + return new DivElement()..classes = ['class-tree-item']
|
| + ..children = [
|
| + new SpanElement()..classes = ['lines'],
|
| + new SpanElement()..classes = ['expander']
|
| + ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
|
| + new SpanElement()..classes = ['name']
|
| + ];
|
| }
|
|
|
| - void _update(Class root) {
|
| - try {
|
| - var rootRow = new ClassTreeRow(isolate, root, tree, null);
|
| - rootRow.children.add(new ClassTreeRow(isolate, root, tree, rootRow));
|
| - tree.initialize(rootRow);
|
| - } catch (e, stackTrace) {
|
| - Logger.root.warning('_update', e, stackTrace);
|
| + void _update(HtmlElement el, M.Class cls, int index) {
|
| + virtualTreeUpdateLines(el.children[0], index);
|
| + if (cls.subclasses.isEmpty) {
|
| + el.children[1].text = '';
|
| + } else {
|
| + el.children[1].text = _tree.isExpanded(cls) ? '▼' : '►';
|
| }
|
| - // Check if we only have one node at the root and expand it.
|
| - if (tree.rows.length == 1) {
|
| - tree.toggle(tree.rows[0]);
|
| + el.children[2].children = [
|
| + new ClassRefElement(_isolate, cls, queue: _r.queue)
|
| + ];
|
| + if (_mixins[cls.id] != null) {
|
| + el.children[2].children.addAll(_createMixins(_mixins[cls.id]));
|
| }
|
| - notifyPropertyChange(#tree, null, tree);
|
| + }
|
| +
|
| + List<Element> _createMixins(List<M.Instance> types) {
|
| + final children = types.expand((type) => [
|
| + new SpanElement()..text = ', ',
|
| + type.typeClass == null
|
| + ? (new SpanElement()..text = type.name.split('<').first)
|
| + : new ClassRefElement(_isolate, type.typeClass, queue: _r.queue)
|
| + ]).toList();
|
| + children.first.text = ' with ';
|
| + return children;
|
| + }
|
| +
|
| + Iterable<M.Class> _children(M.Class cls) {
|
| + return _subclasses[cls.id];
|
| }
|
| }
|
|
|