devtools
Add some simple side bar functionality with a mock editor environment
#6104
Merged

Add some simple side bar functionality with a mock editor environment #6104

DanTup
DanTup2 years ago

This is (another) evolution of #5747 / #5921.

I wanted to make it easy to work on this panel without needing to embed inside VS Code (not just because messing around with connecting WebSockets is clunky, but because it also means having Dart-Code set up for development). I tried hosting the panel inside an iframe (where the outer page could simulate the editor), but hosting Flutter apps inside an iframe seems to break the debugging flow (the inner app fails to start up with some odd javascript errors, but I think the debugger would fail to work with it even with that fixes).

So this version hosts everything in one page by just wrapping the VsCodeFlutterPanel with another widget (currently called VsCodeFlutterPanelMock but it's not a great name) which plays the part of the IDE. It sets up streams (to emulate postMessage, WebSockets, whatever) and logs the data going over it. It provides a mock implementation of the API that VS Code will provide and some buttons to trigger events that would originate in VS Code.

This should allow working on the panel and defining a concrete API (but adding it to the DartApi classes and a mock implementation), as well as making it possible to write tests, before implementing the same API in VS Code.

Recording.2023-07-26.183432.mp4

@kenzieschmoll interested in your thoughts. Right now the bits that are there are very basic (for example the devices passed over only have IDs), but I think it'll be quicker to iterate like this without having to simultaneously provide the VS Code implementation.

DanTup
DanTup1 year ago

@kenzieschmoll I'm reworking this a little bit (to make the API a bit cleaner, support capabilities, and to make the API interface clearer so it can be kept in-sync with VS Code). Feel free to skip looking at this until I'm done (although the general idea is the same, and I'd still like to have a "mock" instance of an editor for easier development (since we'd likely end up with something similar for automated test anyway).

DanTup DanTup force pushed from e6676644 to d9ee815a 1 year ago
DanTup DanTup changed the title WIP: Add some simple side bar functionality with a mock editor environment Add some simple side bar functionality with a mock editor environment 1 year ago
DanTup
DanTup1 year ago

@kenzieschmoll I've pushed some changes here. It's not currently visually appealing, but the basic framework is there. I have a branch of Dart-Code that costs the sidebar and provides the same functionality that the mock environment does here.

This currently uses postMessage inside VS Code and just simple streams in the mock environment - both are just JSON-RPC so I believe should be an easy migration to support DTD in the future.

APIs are implemented in a way that is conditional (so the panel can check which APIs are available) and supports capabilities (so if a whole API is available, we can still check its capabilities to help with the API changing over time). I separated the APIs into an interface and an implementation so that breaking changes should be more obvious and it's clear exactly what needs to be mirrored between here and the other side.

It'll need tests + making look nicer, but I'd like to get some feedback on the general approach first. You can run this using the new launch configuration here (which will load the mock environment, showing the embedded panel and some buttons/log stream):

image

kenzieschmoll
kenzieschmoll commented on 2023-08-02
kenzieschmoll1 year ago

can you mark which parts of this prototype would become obsolete once we have a Dart Tooling Daemon we can use to pass messages back and forth?

DanTup
DanTup1 year ago

can you mark which parts of this prototype would become obsolete once we have a Dart Tooling Daemon we can use to pass messages back and forth?

I don't actually think much of this will go away, but rather just be changed slightly. For example the DartToolingApiImpl.postMessage() constructor would likely be replaced with one that connects to the DTD instead, and the helpers for sending requests/listening to streams (DartToolingApiImpl.sendRequest and DartToolingApiImpl.events) might change slightly but I think everything else will probably remain (we still want the API classes and JSON handling for type safety - although these are something we might be able to code-gen - but for now they're hand-written).

kenzieschmoll
kenzieschmoll commented on 2023-08-03
packages/devtools_app/lib/src/standalone_ui/api/dart_tooling_api.dart
4
5import 'vs_code_api.dart';
6
7
/// An API exposed to Dart tooling extensions.
kenzieschmoll1 year ago

nit: replace "extensions" with "surfaces". This could be confusing with DevTools Extensions.

DanTup1 year ago

Done! Although I'm not sure these terms are completely clear to me (surfaces vs extensions).

I was thinking "Dart Tooling Extensions" would mean any extension that could consume the new APIs (via DTD), and that "DevTools Extensions" would be a subset of "Dart Tooling Extensions".

packages/devtools_app/lib/src/standalone_ui/api/impl/dart_tooling_api.dart
29
30final _log = Logger('tooling_api');
31
32
/// An API for interacting with Dart tooling.
kenzieschmoll1 year ago

what exactly is "Dart tooling" here? DevTools, Dart-Code VS code extension?

DanTup1 year ago

I changed this to:

/// An API used by Dart tooling surfaces to interact with Dart tools that expose
/// APIs such as Dart-Code and the LSP server.

Is that clearer? I couldn't come up with a (non-vague) term to describe the components that will generally provide APIs.

packages/devtools_app/lib/src/standalone_ui/api/impl/dart_tooling_api.dart
56 return DartToolingApiImpl.rpc(json_rpc_2.Peer.withoutJson(channel));
57 }
58
59
/// Connects the API over the provided WebSocket.
kenzieschmoll1 year ago

can you add a comment describing when we would want to use the websocket connection vs the post message connection.

DanTup1 year ago

I've removed this as it was unused. It was originally used to let you connect to VS Code without being embedded inside it (as a way to simplify debugging when interacting with VS Code), however I think that's somewhat superseded by the mock environment - and if we do want to support it in future we'll just use the DTD.

packages/devtools_app/lib/src/standalone_ui/api/impl/dart_tooling_api.dart
102 @protected
103 Future<T> sendRequest<T>(String method, [Object? parameters]) async {
104 return (await rpc.sendRequest('$apiName.$method', parameters)) as T;
105
}
kenzieschmoll1 year ago

does this stream controller need to be created in the context of the class so that it can be disposed once it is done being used?

DanTup1 year ago

I've added a line here to automatically close it when rpc closes which I think does what you wanted (but also handles the connection going away without dispose being called explicitly).

packages/devtools_app/lib/src/standalone_ui/api/impl/vs_code_api.dart
kenzieschmoll1 year ago

what is the benefit of having an interface for all these classes? Can we just have one class instead of having an interface and an impl?

DanTup1 year ago

The goal was to make the API much clearer so it can more easily be mirrored/compared to VS Code. Without interfaces it might also be quite easy to make an accidental breaking change to the interface (and the mock environment) without VS Code.

I added some more comments to the interface classes to make this clearer, although I'm not married to the idea - if you'd prefer not to have them (and aren't concerned about accidental breakage), I'm happy to remove :-)

packages/devtools_app/lib/src/standalone_ui/api/impl/vs_code_api.dart
66
67 VsCodeDeviceImpl.fromJson(Map<String, Object?> json)
68 : this(
69
id: json['id'] as String,
70
name: json['name'] as String,
71
category: json['category'] as String?,
72
emulator: json['emulator'] as bool,
73
emulatorId: json['emulatorId'] as String?,
74
ephemeral: json['ephemeral'] as bool,
75
platform: json['platform'] as String,
76
platformType: json['platformType'] as String?,
kenzieschmoll1 year ago

put these strings into consts so that we can reuse them here and in toJson below

DanTup1 year ago

Done! I put them in the API interface class for now (since they're really part of the API) but if we combine them they'll come back here. FWIW I'd really prefer to code-gen this because there's a lot of boilerplate (that would be copy/pasted and easy to have mistakes), but probably something for later.

packages/devtools_app/lib/src/standalone_ui/api/impl/vs_code_api.dart
120
121 VsCodeDevicesEventImpl.fromJson(Map<String, Object?> json)
122 : this(
123
selectedDeviceId: json['selectedDeviceId'] as String?,
124
devices: (json['devices'] as List)
kenzieschmoll1 year ago

same here, please make these static consts for re-use below

DanTup1 year ago

Done!

packages/devtools_app/lib/src/standalone_ui/api/impl/vs_code_api.dart
38 @override
39 Future<Object?> executeCommand(String command, [List<Object?>? arguments]) {
40 return sendRequest(
41
'executeCommand',
kenzieschmoll1 year ago

put this and 'selectDevice' into a const class member

DanTup1 year ago

Done!

packages/devtools_app/lib/src/standalone_ui/api/impl/vs_code_api.dart
kenzieschmoll1 year ago

should there also be a capability for 'selectDevice'

DanTup1 year ago

Good spot, fixed!

packages/devtools_app/lib/src/standalone_ui/vs_code/flutter_panel.dart
24 State<VsCodeFlutterPanel> createState() => _VsCodeFlutterPanelState();
25}
1126
27
class _VsCodeFlutterPanelState extends State<VsCodeFlutterPanel> {
kenzieschmoll1 year ago

I don't think this needs to be a stateful widget

DanTup1 year ago

You're right, fixed!

packages/devtools_app/lib/src/standalone_ui/vs_code/flutter_panel.dart
33
34 return Column(
35 children: [
36
const Text(''),
kenzieschmoll1 year ago

can we remove this empty text widget?

DanTup1 year ago

I replaced this with const SizedBox(height: 20), (it was to add some padding to prevent the first item in the column touching the top of the window), but I'm not sure if that's the best way to do this?

packages/devtools_app/lib/src/standalone_ui/vs_code/flutter_panel.dart
83 stream: widget.api.devicesChanged,
84 builder: (context, snapshot) {
85 if (!snapshot.hasData) {
86
return const Text('');
kenzieschmoll1 year ago👍 1

return SizedBox.shrink() for a placeholder instead

packages/devtools_app/lib/src/standalone_ui/vs_code/flutter_panel.dart
97 onPressed: () =>
98 unawaited(widget.api.selectDevice(device.id)),
99 ),
100
Text(
101
device.id == deviceEvent.selectedDeviceId
102
? '(selected)'
103
: '',
104
),
kenzieschmoll1 year ago

this text can be part of the TextButton child Text widget above.

Text(
  '${device.name}'
  '${device.id == deviceEvent.selectedDeviceId ? ' (selected)' : ''}',
)
DanTup1 year ago

That would include it in the button so the styling would change - this was deliberately separate:

image

Although, this "design" is just a placeholder and we definitely don't want to ship it looking like this (we probably want to show more than just the name of a device for ex.).

kenzieschmoll1 year ago

Fine to land as is and iterate with some ux input later

packages/devtools_app/lib/src/standalone_ui/vs_code/flutter_panel.dart
86 return const Text('');
87 }
88 final deviceEvent = snapshot.data!;
89
return Table(
kenzieschmoll1 year ago

can we use a ListView or a column instead?

packages/devtools_app/lib/src/standalone_ui/vs_code/mock_environment/dart_tooling_mock_api.dart
84 emulator: false,
85 emulatorId: null,
86 ephemeral: false,
87
platform: 'dartwin-x64',
kenzieschmoll1 year ago😄 1

darwin or dartwin?

packages/devtools_app/lib/src/standalone_ui/vs_code/mock_environment/dart_tooling_mock_api.dart
100 ];
101
102 /// The current set of devices being presented to the embedded panel.
103
final List<VsCodeDevice> _devices = [];
kenzieschmoll1 year ago👍 1

nit: put type on right hand side:
final _devices = <VsCodeDevice>[]

packages/devtools_app/lib/src/standalone_ui/vs_code/mock_environment/flutter_panel_mock_editor.dart
17///
18/// This UI interacts with [MockDartToolingApi] to allow triggering events that
19/// would normally be fired by the IDE and also shows a log of recent requests.
20
class VsCodeFlutterPanelMockEditor extends StatefulWidget {
kenzieschmoll1 year ago

I wonder if this should be implemented as a stager app that lives in our test/ directory, that way it doesn't have to get shipped with our production code. We have stager apps for several DevTools screens that can be used for simplified development and testing.

DanTup1 year ago

Oh that might be better. I'm not very familiar with stager though - I'll take a look at changing it this week. It would definitely be nicer if we could keep development screen out of the main app. Thanks!

DanTup1 year ago

I've done this, so the mock API + mock environment now live in test/. I need to figure out why the theming doesn't work though (it seems to always run in light mode even when the toggle is set to dark).

packages/devtools_app/lib/src/standalone_ui/api/impl/vs_code_api.dart
3// found in the LICENSE file.
4
5import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc_2;
6
import 'package:meta/meta.dart';
kenzieschmoll1 year ago

do we need to add a dep on meta? I remember an effort to remove this dependency from devtools a while back, though I can't recall why. Probably because we were using it for @required and some other signatures that are now provided by flutter out of the box.

DanTup1 year ago (edited 1 year ago)

I added it to use @protected to try and avoid some of the base methods in the API classes showing up to the widget code. Unfortunately code completion still shows them (dart-lang/sdk#35449), though it does generate a warning if you use them.

It was mostly just to enforce everything going through methods (eg. don't call sendRequest('someString') directly from a widget), but it's a fairly small benefit. If there's a reason to avoid the dependency (and it wasn't just removed because it was unnecessary), we could remove it. I tracked the change back to #3484 but I don't understand the reason - wdyt?

DanTup DanTup force pushed from 4b573cea to 8dd4eaac 1 year ago
kenzieschmoll
kenzieschmoll commented on 2023-08-10
packages/devtools_app/lib/src/standalone_ui/vs_code/flutter_panel.dart
7365 Widget build(BuildContext context) {
7466 return Column(
7567 children: [
68
const SizedBox(height: 20),
kenzieschmoll1 year ago👍 1

nit use defaultSpacing so that we are consistent with other devtools spacing

kenzieschmoll
kenzieschmoll commented on 2023-08-10
packages/devtools_app/test/test_infra/scenes/standalone_ui/vs_code_mock_editor.dart
86108 initialFractions: const [0.5, 0.5],
87109 minSizes: const [200, 200],
110 splitters: [
111
PreferredSize(
112
preferredSize: const Size.fromHeight(1),
113
child: SizedBox(
114
height: 1,
115
child:
116
ColoredBox(color: editorTheme.editorTerminalSplitterColor),
117
),
118
),
119
],
kenzieschmoll1 year ago

nit: do we need to manually specify a splitter here? is the DefaultSplitter that is used when splitters is null not sufficient?

DanTup1 year ago

Not really. I was trying to make it look a little more like VS Code (so it gave a better idea of how it'd look) so this was to set the colour and make it narrower. It didn't work particularly well though, so I've removed it for now.

kenzieschmoll
kenzieschmoll commented on 2023-08-10
kenzieschmoll1 year ago (edited 1 year ago)

This is looking good. I'm happy we were able to move the mock environment into a stager app so that it is not part of our production code. We'll likely need some clean up and iteration on the UI, but okay to land and iterate while we build these embedded features out.

DanTup DanTup added run-dcm-workflow
DanTup DanTup added release-notes-not-required
DanTup DanTup marked this pull request as ready for review 1 year ago
DanTup DanTup requested a review 1 year ago
DanTup DanTup removed review request 1 year ago
DanTup DanTup requested a review from CoderDake CoderDake 1 year ago
kenzieschmoll
kenzieschmoll approved these changes on 2023-08-17
DanTup Add some simple side bar functionality with a mock editor environment
34b5a8d8
DanTup Add additional comments
32bb16d6
DanTup Minor review comments
c1db587f
DanTup Move mock environment to test/stager scene
9d90cccb
DanTup Fix "Dark Theme" toggle in VS Code panel in stager scene
e560948d
DanTup Use defaultSpacing + remove custom split widgets
b32f765e
DanTup Remove unused key parameter
7aaf25f3
DanTup DanTup force pushed from ef9cb40e to 7aaf25f3 1 year ago
DanTup
DanTup1 year ago

The changes above this comment were just a rebase with no changes (because it's been a while since I opened this). Seems like a few things have changed in master and there are some errors, so the changes after this comment will be real changes to fix them.

DanTup Update imports
074eb478
DanTup
DanTup1 year ago

Turned out to just be needing imports for package:devtools_app_shared/ui.dart and no other changes, so merging.

DanTup DanTup merged 6440a758 into master 1 year ago

Login to write a write a comment.

Login via GitHub

Assignees
No one assigned
Labels
Milestone