-
Notifications
You must be signed in to change notification settings - Fork 337
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
Add some simple side bar functionality with a mock editor environment #6104
Conversation
@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). |
e667664
to
d9ee815
Compare
@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): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
|
||
import 'vs_code_api.dart'; | ||
|
||
/// An API exposed to Dart tooling extensions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: replace "extensions" with "surfaces". This could be confusing with DevTools Extensions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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".
|
||
final _log = Logger('tooling_api'); | ||
|
||
/// An API for interacting with Dart tooling. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what exactly is "Dart tooling" here? DevTools, Dart-Code VS code extension?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
return DartToolingApiImpl.rpc(json_rpc_2.Peer.withoutJson(channel)); | ||
} | ||
|
||
/// Connects the API over the provided WebSocket. |
There was a problem hiding this comment.
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 describing when we would want to use the websocket connection vs the post message connection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
/// Listens for an event '[apiName].[name]' that has a Map for parameters. | ||
@protected | ||
Stream<Map<String, Object?>> events(String name) { | ||
final streamController = StreamController<Map<String, Object?>>.broadcast(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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).
} | ||
} | ||
|
||
class VsCodeDeviceImpl implements VsCodeDevice { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 :-)
return const Text(''); | ||
} | ||
final deviceEvent = snapshot.data!; | ||
return Table( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we use a ListView or a column instead?
emulator: false, | ||
emulatorId: null, | ||
ephemeral: false, | ||
platform: 'dartwin-x64', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
darwin or dartwin?
]; | ||
|
||
/// The current set of devices being presented to the embedded panel. | ||
final List<VsCodeDevice> _devices = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: put type on right hand side:
final _devices = <VsCodeDevice>[]
/// | ||
/// This UI interacts with [MockDartToolingApi] to allow triggering events that | ||
/// would normally be fired by the IDE and also shows a log of recent requests. | ||
class VsCodeFlutterPanelMockEditor extends StatefulWidget { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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).
// found in the LICENSE file. | ||
|
||
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc_2; | ||
import 'package:meta/meta.dart'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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?
4b573ce
to
8dd4eaa
Compare
@@ -73,41 +65,43 @@ class _VsCodeConnectedPanelState extends State<_VsCodeConnectedPanel> { | |||
Widget build(BuildContext context) { | |||
return Column( | |||
children: [ | |||
const SizedBox(height: 20), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit use defaultSpacing
so that we are consistent with other devtools spacing
PreferredSize( | ||
preferredSize: const Size.fromHeight(1), | ||
child: SizedBox( | ||
height: 1, | ||
child: | ||
ColoredBox(color: editorTheme.editorTerminalSplitterColor), | ||
), | ||
), | ||
], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: do we need to manually specify a splitter here? is the DefaultSplitter
that is used when splitters
is null not sufficient?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
- Add comments - Use more constants - StatelessWidget where state not required - Fix typos - Add `selectDevice` capability - ...
ef9cb40
to
7aaf25f
Compare
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. |
Turned out to just be needing imports for |
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 calledVsCodeFlutterPanelMock
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.