in flutter-idea/src/io/flutter/view/InspectorPanel.java [867:969]
public void updateSelectionFromService(boolean textEditorUpdated) {
treeLoadStarted = true;
selectionGroups.cancelNext();
final CompletableFuture<DiagnosticsNode> pendingSelectionFuture =
selectionGroups.getNext().getSelection(getSelectedDiagnostic(), treeType, isSummaryTree);
CompletableFuture<?> allPending;
final CompletableFuture<DiagnosticsNode> pendingDetailsFuture =
isSummaryTree ? selectionGroups.getNext().getSelection(getSelectedDiagnostic(), treeType, false) : null;
final CompletableFuture<?> selectionsReady =
isSummaryTree ? CompletableFuture.allOf(pendingDetailsFuture, pendingSelectionFuture) : pendingSelectionFuture;
selectionGroups.getNext().safeWhenComplete(selectionsReady, (ignored, error) -> {
if (error != null) {
FlutterUtils.warn(LOG, error);
selectionGroups.cancelNext();
return;
}
selectionGroups.promoteNext();
final DiagnosticsNode newSelection = pendingSelectionFuture.getNow(null);
if (!legacyMode) {
DiagnosticsNode detailsSelection = null;
if (pendingDetailsFuture != null) {
detailsSelection = pendingDetailsFuture.getNow(null);
}
subtreeRoot = newSelection;
applyNewSelection(newSelection, detailsSelection, true, textEditorUpdated);
}
else {
// Legacy case. TODO(jacobr): deprecate and remove this code.
// This case only exists for the RenderObject tree which we haven't updated yet to use the new UI style.
// TODO(jacobr): delete it.
if (newSelection == null) {
recomputeTreeRoot(null, null, false, true);
return;
}
// TODO(jacobr): switch to using current and next groups in this case
// as well to avoid memory leaks.
treeGroups.getCurrent()
.safeWhenComplete(treeGroups.getCurrent().getParentChain(newSelection), (ArrayList<DiagnosticsPathNode> path, Throwable ex) -> {
if (ex != null) {
FlutterUtils.warn(LOG, ex);
return;
}
DefaultMutableTreeNode treeNode = getRootNode();
final DefaultTreeModel model = getTreeModel();
final DefaultMutableTreeNode[] treePath = new DefaultMutableTreeNode[path.size()];
for (int i = 0; i < path.size(); ++i) {
treePath[i] = treeNode;
final DiagnosticsPathNode pathNode = path.get(i);
final DiagnosticsNode pathDiagnosticNode = pathNode.getNode();
final ArrayList<DiagnosticsNode> newChildren = pathNode.getChildren();
final DiagnosticsNode existingNode = getDiagnosticNode(treeNode);
if (!identicalDiagnosticsNodes(pathDiagnosticNode, existingNode)) {
treeNode.setUserObject(pathDiagnosticNode);
}
treeNode.setAllowsChildren(!newChildren.isEmpty());
for (int j = 0; j < newChildren.size(); ++j) {
final DiagnosticsNode newChild = newChildren.get(j);
if (j >= treeNode.getChildCount() || !identicalDiagnosticsNodes(newChild, getDiagnosticNode(treeNode.getChildAt(j)))) {
final DefaultMutableTreeNode child;
if (j >= treeNode.getChildCount()) {
child = new DefaultMutableTreeNode();
treeNode.add(child);
}
else {
child = (DefaultMutableTreeNode)treeNode.getChildAt(j);
}
if (j != pathNode.getChildIndex()) {
setupTreeNode(child, newChild, false);
model.reload(child);
}
else {
child.setUserObject(newChild);
child.setAllowsChildren(newChild.hasChildren());
child.removeAllChildren();
}
// TODO(jacobr): we are likely calling the wrong node structure changed APIs.
// For example, we should be getting these change notifications for free if we
// switched to call methods on the model object directly to manipulate the tree.
model.nodeStructureChanged(child);
model.nodeChanged(child);
}
model.reload(treeNode);
}
if (i != path.size() - 1) {
treeNode = (DefaultMutableTreeNode)treeNode.getChildAt(pathNode.getChildIndex());
}
}
// TODO(jacobr): review and simplify this.
final TreePath selectionPath = new TreePath(treePath);
myRootsTree.setSelectionPath(selectionPath);
animateTo(treePath[treePath.length - 1]);
});
}
});
}