Skip to content

Fixes broken program explorer on hot restart #3527

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 10, 2021

Conversation

elliette
Copy link
Member

@elliette elliette commented Dec 9, 2021

Fixes #3521

@elliette elliette changed the title Fixes broken program explorer on restart Fixes broken program explorer on hot restart Dec 9, 2021
_initialized.value = true;
}

void initListeners() {
addAutoDisposeListener(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was removed because it was redundant - the debugger_controller was also listening for serviceManager.isolateManager.selectedIsolate code link, and was re-populating the scripts when the isolate changed. Therefore, what was happening was:

  1. isolate changes -> refresh
  2. sorted scripts changes -> refresh

Since we only want to refresh once we have the scripts, this first listener could be removed.

@@ -63,10 +63,8 @@ class _TreeViewState<T extends TreeNode<T>> extends State<TreeView<T>>
@override
void didUpdateWidget(TreeView<T> oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.dataRoots != oldWidget.dataRoots) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this check was ever true. It seems like the oldWidget.dataRoots were always identical to the widget.dataRoots, regardless of whether the dataRoots had changed or not. This meant the tree view wasn't being re-drawn on refresh.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked into this for the uses of TreeView throughout devtools. I think we should change TreeView to accept dataRootsListenable instead of dataRoots. Then all the ValueListenableBuilders around TreeView could be removed, and TreeView would be responsible for listening to changes in dataRootsListenable and calling setState. The one place where we aren't using a List<T> listenable for the tree view is ExpandableVariable (where the listenable has type T not a List<T>). For this case you can just pass in the raw variable to ExpandableVariable and use a FixedValueListenable with this value for the TreeView

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also as a cleanup as part of this change, looks like the VariablesList class can be deleted

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, thanks! Also deleted VariablesList

@@ -130,6 +126,7 @@ class ProgramExplorerController extends DisposableController

/// Marks [node] as the currently selected node, clearing the selection state
/// of any currently selected node.
/// TODO(elliette): Scroll to node when selected.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a regression? this was originally implemented by @bkonyi here #3480

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks like it implements scrolling within the file, this comment is for scrolling within the program explorer tree view - eg: user opens some_file.dart at the bottom of the tree view, and then does a hot-restart. On restart, main.dart is selected and opened, but the tree is still scrolled to some_file.dart. I've added more details to the comment to make it clearer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it. On another note, after a hot restart, should the selected and opened file be some_file.dart if it is still available instead of main.dart?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm good point - added a TODO to see if that's possible

/// to calling .clear() then .addAll(), because it only notifies listeners
/// once.
void replaceAll(Iterable<T> elements) {
_rawList.clear();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's use _rawList = <T>[]; here to be consistent with how the clear method clears out the list

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, done!

// TODO(kenz): preserve expanded state of tree on switching frames and
// on stepping.
return TreeView<DartObjectNode>(
dataRootsListenable: ValueNotifier([variable]),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use FixedValueListenable here instead

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -56,21 +57,30 @@ class _TreeViewState<T extends TreeNode<T>> extends State<TreeView<T>>
@override
void initState() {
super.initState();
_initData();
_updateItems();
widget.dataRootsListenable.addListener(_updateTreeView);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you use addAutoDisposeListener instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}

// @override
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove commented out code

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops thanks!

void _initData() {
dataRoots = List.from(widget.dataRoots);
dataRoots = List.from(widget.dataRootsListenable.value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just put this directly in _updateTreeView since _initData is not used anywhere else

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Member

@kenzieschmoll kenzieschmoll left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM after comments

return ValueListenableBuilder<List<DartObjectNode>>(
valueListenable: controller.variables,
builder: (context, variables, _) {
if (variables.isEmpty) return const SizedBox();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can actually remove the ValueListenableBuilder and the special case for variables.isEmpty. TreeView returns a SizedBox in its build method when there is an empty data set

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh nice! Good point

builder: (context, nodes, _) {
if (nodes == null || nodes.isEmpty) {
return const Center(
child: Text('Nothing to inspect'),
Copy link
Member

@kenzieschmoll kenzieschmoll Dec 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add an optional parameter to TreeView, emptyTreeDisplayBuilder or some better name if you can think of one that defaults to building const SizedBox(). Then use this value in the TreeView build method when the data is empty. I just took a look at the code, and I don't think controller.outlineNodes.value will ever be null, so we shouldn't have to worry about the null case.

Then you can remove the ValueListenableBuilder here as well. As is, there are two listeners for controller.outlineNodes when there only needs to be one (in TreeView)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, added optional parameter

@elliette elliette merged commit 4a99031 into flutter:master Dec 10, 2021
@elliette elliette deleted the issue-3521 branch December 24, 2021 00:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

File explorer does not show file contents after a hot restart
2 participants