Skip to content

Replace ScriptPicker with ProgramExplorer on DebuggerScreen #3227

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 28 commits into from
Sep 24, 2021

Conversation

bkonyi
Copy link
Contributor

@bkonyi bkonyi commented Jul 29, 2021

This change replaces the current ScriptPicker implementation with a more fully featured ProgramExplorer.

Functionality:

  • Navigate the program structure, including libraries, scripts, classes, functions, and fields
  • Display generic parameter information for generic classes and functions
  • View function and field signatures in the program explorer
  • Color-coded icons for each object type to match existing syntax highlighting
  • Jump to definition of classes, functions, and fields
  • Lazy population of class nodes
  • Filtering based on object names

Additional Changes:

  • Updated CodeView to allow for scrolling to a location aligned to the top of the view
  • Fixed issue where CodeView background was transparent
  • TreeView nodes are now highlighted when selected
  • Added support for different behaviors for TreeView selection vs expansion

Future Work:

  • Display code objects in VM developer mode
  • Allow for inspecting object information in an "object inspector" window in the debugger page

@bkonyi bkonyi requested review from kenzieschmoll and jacob314 July 29, 2021 21:41
@bkonyi
Copy link
Contributor Author

bkonyi commented Jul 29, 2021

@kenzieschmoll @jacob314 I think this is ready for an initial pass. PTAL when you have a chance.

@kenzieschmoll
Copy link
Member

Can you add a screenshot of the new program explorer to the PR description?

@@ -78,6 +80,13 @@ class _CodeViewState extends State<CodeView>
gutterController = verticalController.addAndGet();
textController = verticalController.addAndGet();

if (widget.initialPosition != null) {
Copy link
Member

Choose a reason for hiding this comment

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

This likely also needs to be in didUpdateWidget so that this state object can manage the scroll position if the widget was updated with a new value for initialPosition

Copy link
Contributor

Choose a reason for hiding this comment

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

agree we need to do something in didUpdateWidget but we will need to be very careful. You don't want to update the scrollPosition unless widget.initialPosition != oldWidget.initialPosition.

@@ -78,6 +80,13 @@ class _CodeViewState extends State<CodeView>
gutterController = verticalController.addAndGet();
textController = verticalController.addAndGet();

if (widget.initialPosition != null) {
final location = widget.initialPosition.location;
final lineIndex = location.line - 1;
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 add a comment as to why we are subtracting 1

@@ -124,7 +124,7 @@ class _DebuggingControlsState extends State<DebuggingControls>
child: Container(
color: visible ? Theme.of(context).highlightColor : null,
child: DebuggerButton(
title: 'Libraries',
title: 'Program Explorer',
Copy link
Member

Choose a reason for hiding this comment

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

I'm not completely sold on this name. Maybe "Source Explorer"? Input here @jacob314 @devoncarew?

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we leave it as Libraries for now? We are just showing more information about the libraries.

@@ -278,6 +285,9 @@ class DebuggerController extends DisposableController
/// Return the sorted list of ScriptRefs active in the current isolate.
ValueListenable<List<ScriptRef>> get sortedScripts => _sortedScripts;

bool get centerScrollLocation => _centerScrollLocation;
Copy link
Member

Choose a reason for hiding this comment

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

is it possible that this value could get accessed for a script it is not associated with? Storing this at the controller level vs the per call level seems problematic

class SourcePosition {
SourcePosition({@required this.line, @required this.column, this.tokenPos});
const SourcePosition(
{@required this.line, @required this.column, this.file, this.tokenPos});
Copy link
Member

Choose a reason for hiding this comment

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

nit: trailing comma

@@ -79,6 +80,7 @@ class DebuggerScreenBodyState extends State<DebuggerScreenBody>
static const breakpointsTitle = 'Breakpoints';

DebuggerController controller;
//ObjectTreeController objectSelectorController = ObjectTreeController();
Copy link
Member

Choose a reason for hiding this comment

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

delete comment?

@@ -399,6 +399,11 @@ extension ThemeDataExtension on ThemeData {
TextStyle get subtleFixedFontStyle =>
fixedFontStyle.copyWith(color: unselectedWidgetColor);

TextStyle get toolTipFixedFontStyle => //colorScheme.isLight ?
Copy link
Member

Choose a reason for hiding this comment

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

remove comment

if (onChange != null) {
onChange(
history.current.value,
current,
Copy link
Member

Choose a reason for hiding this comment

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

why not implement a peekBack method to mirror what is done above with moveBack and peekNext?

@kenzieschmoll
Copy link
Member

At a high level, is this change something we have discussed in the past? It seems inconsistent with other discussions we've had regarding debugger UI improvements (see #3075). This is probably something that could use some UX input on (@InMatrix). @devoncarew @jacob314 for input on using a program explorer (classes, functions, fields, etc.) vs a file tree explorer in the debugger (described in linked issue #3075).

My concern is making this view more complicated and thus more difficult for users to find what they are looking for. Do users expect to see a file explorer or a program explorer in a debugger? Should we make displaying low level details (classes, functions, etc.) an optional setting?

@InMatrix
Copy link

InMatrix commented Aug 3, 2021

@bkonyi Could you post some before-and-after screenshots to help me better understand this change?

@bkonyi
Copy link
Contributor Author

bkonyi commented Aug 3, 2021

There wasn't an explicit discussion, but I did chat with @jacob314 offline. In general, we need to be able to examine details of fields, functions, and classes to reach feature parity with Observatory (that work isn't included in this PR as the explorer was already a big change) in addition to examining code objects (in VM developer mode). Inspecting code objects will require a code view in addition to an object details view, so the debugger pane seemed like the right place for this rather than creating another top-level tab.

Here's some screenshots:

Before:
Screen Shot 2021-08-03 at 7 25 19 PM
Screen Shot 2021-08-03 at 7 25 33 PM

After:
Screen Shot 2021-08-03 at 7 22 27 PM
Screen Shot 2021-08-03 at 7 23 14 PM
Screen Shot 2021-08-03 at 7 23 43 PM

@bkonyi
Copy link
Contributor Author

bkonyi commented Aug 3, 2021

@InMatrix it's not clear from the screenshots but selecting classes, functions, and fields currently jumps to their definition in the script.

@kenzieschmoll
Copy link
Member

Some things that are confusing to me:
Screen Shot 2021-08-04 at 9 43 18 AM

  1. package and file names are repeated (e.g. dart:http & dart:http on the same line item). Perhaps for packages and files, we could densify the line item and just show the package name (as one line, not two), and file names can just have the file name without the whole path (the path could be displayed as a tooltip or something). This would make the UI look more like a tree file explorer at the package / file level.
  2. Icons for packages / paths should be more basic (e.g. a folder icon for packages and a file or page icon for a file). For example:
    Screen Shot 2021-08-04 at 9 48 03 AM

Icons for underlying classes, functions, etc should be more descriptive - for example I like the sigma icon for functions.

@bkonyi
Copy link
Contributor Author

bkonyi commented Aug 4, 2021

  1. package and file names are repeated (e.g. dart:http & dart:http on the same line item). Perhaps for packages and files, we could densify the line item and just show the package name (as one line, not two), and file names can just have the file name without the whole path (the path could be displayed as a tooltip or something). This would make the UI look more like a tree file explorer at the package / file level.

Sure, that definitely makes sense. It looked a bit "empty" to me without the second row, but it's not essential.

  1. Icons for packages / paths should be more basic (e.g. a folder icon for packages and a file or page icon for a file).

The icons for libraries and folders are identical to the icons in the original script picker :-)

Icons for underlying classes, functions, etc should be more descriptive - for example I like the sigma icon for functions.

I agree 1000%. I basically scanned through the Flutter material icons and picked ones that looked okay, so we could definitely use some design input for those.

@jacob314
Copy link
Contributor

jacob314 commented Aug 9, 2021

With regards to #3075, I don't see this in conflict with it. We won't use this view or the script picker for searching but the view will be more useful than the existing ScriptPicker for browsing.

@@ -580,7 +603,7 @@ class _LinesState extends State<Lines> with AutoDisposeMixin {

if (isOutOfViewTop || isOutOfViewBottom) {
// Scroll this search token to the middle of the view.
final targetOffset = math.max(
final targetOffset = math.max<double>(
Copy link
Contributor

Choose a reason for hiding this comment

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

why was this type added? seems like it should be inferred.

final Map<String, VMServiceObjectNode> _childrenAsMap = {};

@override
bool get isExpandable => super.isExpandable || object is ClassRef;
Copy link
Contributor

Choose a reason for hiding this comment

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

what if a class has zero members?

@jacob314
Copy link
Contributor

jacob314 commented Aug 9, 2021

I would suggest aligning the icons with what the structure and project views in IntelliJ or VSCode use.
There are existing icons for files, classes, and methods that users should be somewhat familiar with.

We might also need an option to toggle whether to show the contents of a library by default or not. I am a little worried that showing the contents of a library by default may make the tree harder to browse. One option would be to switch to a master-details style view where we show the list of libraries separate from the "structure" view that shows the contents of the selected library but I don't have a strong opinion on that.
Screen Shot 2021-08-09 at 3 16 33 PM

@kenzieschmoll
Copy link
Member

+1 to Jacob's last comment regarding splitting this view up into two views: file explorer and structure.

@@ -43,6 +43,8 @@ abstract class TreeNode<T extends TreeNode<T>> {

bool get isRoot => parent == null;

bool get isSelected => false;
Copy link
Member

Choose a reason for hiding this comment

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

Should this getter be on the subclass instead of in the TreeNode base class? seems odd to have a getter return a set bool value when there aren't also methods in this TreeNode class to modify the state of isSelected

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this needs to be here since _TreeViewItemState uses this to determine whether or not to highlight a given TreeNode item.

Copy link
Member

Choose a reason for hiding this comment

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

if that is the case, then we should move the logic for managing the state of isSelected into this class as well. Subclasses can always override and call super if they need to do additional work on changing selection, in which case we'd want to annotate with @mustCallSuper

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.

Done with review

setExpanded(widget.data.isExpanded);
}

void _onSelected() {
widget.onItemSelected(widget.data);
widget?.onItemSelected(widget.data);
setExpanded(widget.data.isExpanded);
Copy link
Member

Choose a reason for hiding this comment

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

should we be calling onItemExpanded here as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, since onItemExpanded corresponds to clicking the >, whereas onItemSelected corresponds to clicking on any other part of the list item.

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.

a few more comments then LGTM

@bkonyi bkonyi merged commit a4852d2 into flutter:master Sep 24, 2021
kenzieschmoll added a commit to kenzieschmoll/devtools that referenced this pull request Oct 1, 2021
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.

4 participants