Skip to content

Commit

Permalink
Add events to receive debug sessions into sidebar (#6237)
Browse files Browse the repository at this point in the history
* Add events to receive debug sessions into sidebar

This adds some events for listing debug sessions in the sidebar and buttons to open DevTools features from them.

As before, this is functional but the UI needs much work.

* Rename DevToolSfeature -> DevToolsPage

* Improve styling

* Review tweaks

- DartDocs
- Update comments
- Use named constants instead of literals
- Add some TODOs

* Review tweaks

- Remove infinite SizedBoxes and use crossAxisAlignment on the parent
- Tweak comments about opening DevTools
  • Loading branch information
DanTup authored Sep 7, 2023
1 parent 8b3d926 commit d4908fe
Show file tree
Hide file tree
Showing 9 changed files with 521 additions and 46 deletions.
1 change: 0 additions & 1 deletion packages/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@
"request": "launch",
"type": "dart",
"program": "devtools_app/test/test_infra/scenes/standalone_ui/vs_code.stager_app.g.dart",
"deviceId": "chrome",
},
{
"name": "attach",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ final class VsCodeApiImpl extends ToolApiImpl implements VsCodeApi {
this.capabilities = VsCodeCapabilitiesImpl(capabilities);
devicesChanged = events(VsCodeApi.jsonDevicesChangedEvent)
.map(VsCodeDevicesEventImpl.fromJson);

debugSessionsChanged = events(VsCodeApi.jsonDebugSessionsChangedEvent)
.map(VsCodeDebugSessionsEventImpl.fromJson);
}

static Future<VsCodeApi?> tryConnect(json_rpc_2.Peer rpc) async {
Expand All @@ -31,6 +34,9 @@ final class VsCodeApiImpl extends ToolApiImpl implements VsCodeApi {
@override
late final Stream<VsCodeDevicesEvent> devicesChanged;

@override
late final Stream<VsCodeDebugSessionsEvent> debugSessionsChanged;

@override
late final VsCodeCapabilities capabilities;

Expand All @@ -52,6 +58,17 @@ final class VsCodeApiImpl extends ToolApiImpl implements VsCodeApi {
{VsCodeApi.jsonSelectDeviceIdParameter: id},
);
}

@override
Future<void> openDevToolsPage(String debugSessionId, String page) {
return sendRequest(
VsCodeApi.openDevToolsPageMethod,
{
VsCodeApi.openDevToolsPageDebugSessionIdParameter: debugSessionId,
VsCodeApi.openDevToolsPagePageParameter: page,
},
);
}
}

class VsCodeDeviceImpl implements VsCodeDevice {
Expand Down Expand Up @@ -114,6 +131,57 @@ class VsCodeDeviceImpl implements VsCodeDevice {
};
}

class VsCodeDebugSessionImpl implements VsCodeDebugSession {
VsCodeDebugSessionImpl({
required this.id,
required this.name,
required this.vmServiceUri,
required this.flutterMode,
required this.flutterDeviceId,
required this.debuggerType,
});

VsCodeDebugSessionImpl.fromJson(Map<String, Object?> json)
: this(
id: json[VsCodeDebugSession.jsonIdField] as String,
name: json[VsCodeDebugSession.jsonNameField] as String,
vmServiceUri:
json[VsCodeDebugSession.jsonVmServiceUriField] as String?,
flutterMode: json[VsCodeDebugSession.jsonFlutterModeField] as String?,
flutterDeviceId:
json[VsCodeDebugSession.jsonFlutterDeviceIdField] as String?,
debuggerType:
json[VsCodeDebugSession.jsonDebuggerTypeField] as String?,
);

@override
final String id;

@override
final String name;

@override
final String? vmServiceUri;

@override
final String? flutterMode;

@override
final String? flutterDeviceId;

@override
final String? debuggerType;

Map<String, Object?> toJson() => {
VsCodeDebugSession.jsonIdField: id,
VsCodeDebugSession.jsonNameField: name,
VsCodeDebugSession.jsonVmServiceUriField: vmServiceUri,
VsCodeDebugSession.jsonFlutterModeField: flutterMode,
VsCodeDebugSession.jsonFlutterDeviceIdField: flutterDeviceId,
VsCodeDebugSession.jsonDebuggerTypeField: debuggerType,
};
}

class VsCodeDevicesEventImpl implements VsCodeDevicesEvent {
VsCodeDevicesEventImpl({
required this.selectedDeviceId,
Expand Down Expand Up @@ -142,6 +210,27 @@ class VsCodeDevicesEventImpl implements VsCodeDevicesEvent {
};
}

class VsCodeDebugSessionsEventImpl implements VsCodeDebugSessionsEvent {
VsCodeDebugSessionsEventImpl({
required this.sessions,
});

VsCodeDebugSessionsEventImpl.fromJson(Map<String, Object?> json)
: this(
sessions: (json[VsCodeDebugSessionsEvent.jsonSessionsField] as List)
.map((item) => Map<String, Object?>.from(item))
.map((map) => VsCodeDebugSessionImpl.fromJson(map))
.toList(),
);

@override
final List<VsCodeDebugSession> sessions;

Map<String, Object?> toJson() => {
VsCodeDebugSessionsEvent.jsonSessionsField: sessions,
};
}

class VsCodeCapabilitiesImpl implements VsCodeCapabilities {
VsCodeCapabilitiesImpl(this._raw);

Expand All @@ -154,4 +243,8 @@ class VsCodeCapabilitiesImpl implements VsCodeCapabilities {
@override
bool get selectDevice =>
_raw?[VsCodeCapabilities.jsonSelectDeviceField] == true;

@override
bool get openDevToolsPage =>
_raw?[VsCodeCapabilities.openDevToolsPageField] == true;
}
115 changes: 115 additions & 0 deletions packages/devtools_app/lib/src/standalone_ui/api/vs_code_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,51 @@
/// [VsCodeCapabilities] to advertise which capabilities are available and
/// handle any changes in behaviour.
abstract interface class VsCodeApi {
/// The capabilities of the instance of VS Code / Dart VS Code extension that
/// we are connected to.
///
/// All API calls should be guarded by checks of capabilities because the API
/// may change over time.
VsCodeCapabilities get capabilities;

/// Informs the VS Code extension we are initialized, allowing it to send
/// initial events to all streams with an initial set of data.
Future<void> initialize();

/// A stream of events for whenever the set of devices (or selected device)
/// change in VS Code.
///
/// An event with initial devices is sent after [initialize] is called.
Stream<VsCodeDevicesEvent> get devicesChanged;

/// A stream of events for whenever the set of debug sessions change or are
/// updated in VS Code.
///
/// An event with initial sessions is sent after [initialize] is called.
Stream<VsCodeDebugSessionsEvent> get debugSessionsChanged;

/// Executes a VS Code command.
///
/// Commands can be native VS Code commands or commands registered by the
/// Dart/Flutter extensions.
///
/// Which commands are available is not part of the API contract so callers
/// should take care when calling APIs that might evolve over time.
Future<Object?> executeCommand(String command, [List<Object?>? arguments]);

/// Changes the current Flutter device.
///
/// The selected device is the same one shown in the status bar in VS Code.
/// Calling this API will update the device for the whole VS Code extension.
Future<bool> selectDevice(String id);

/// Opens a specific DevTools [page] for the debug session with ID
/// [debugSessionId].
///
/// Depending on user settings, this may open embedded (the default) or in an
/// external browser window.
Future<void> openDevToolsPage(String debugSessionId, String page);

static const jsonApiName = 'vsCode';

static const jsonInitializeMethod = 'initialize';
Expand All @@ -27,6 +66,12 @@ abstract interface class VsCodeApi {

static const jsonSelectDeviceMethod = 'selectDevice';
static const jsonSelectDeviceIdParameter = 'id';

static const openDevToolsPageMethod = 'openDevToolsPage';
static const openDevToolsPageDebugSessionIdParameter = 'debugSessionId';
static const openDevToolsPagePageParameter = 'page';

static const jsonDebugSessionsChangedEvent = 'debugSessionsChanged';
}

/// This class defines a device exposed by the Dart/Flutter extensions in VS
Expand Down Expand Up @@ -55,30 +100,100 @@ abstract interface class VsCodeDevice {
static const jsonPlatformTypeField = 'platformType';
}

/// This class defines a debug session exposed by the Dart/Flutter extensions in
/// VS Code (and must match the implementation there).
///
/// All changes to this file should be backwards-compatible and use
/// [VsCodeCapabilities] to advertise which capabilities are available and
/// handle any changes in behaviour.
abstract interface class VsCodeDebugSession {
String get id;
String get name;
String? get vmServiceUri;

/// The mode the app is running in.
///
/// These values are defined by Flutter and at the time of writing can include
/// 'debug', 'profile', 'release' and 'jit_release'.
///
/// This value may be unavailable (`null`) for Dart/Test sessions or those
/// that have not fully started yet.
String? get flutterMode;

/// The ID of the device the Flutter app is running on, if available.
String? get flutterDeviceId;

/// The type of debugger session. If available, this is usually one of:
///
/// - Dart (dart run)
/// - DartTest (dart test)
/// - Flutter (flutter run)
/// - FlutterTest (flutter test)
/// - Web (webdev serve)
/// - WebTest (webdev test)
String? get debuggerType;

static const jsonIdField = 'id';
static const jsonNameField = 'name';
static const jsonVmServiceUriField = 'vmServiceUri';
static const jsonFlutterModeField = 'flutterMode';
static const jsonFlutterDeviceIdField = 'flutterDeviceId';
static const jsonDebuggerTypeField = 'debuggerType';
}

/// This class defines a device event sent by the Dart/Flutter extensions in VS
/// Code (and must match the implementation there).
///
/// All changes to this file should be backwards-compatible and use
/// [VsCodeCapabilities] to advertise which capabilities are available and
/// handle any changes in behaviour.
abstract interface class VsCodeDevicesEvent {
/// The ID of the selected Flutter device in VS Code.
///
/// This device can be changed with the `selectDevice` method but can also
/// be changed by the VS Code extension (which will emit a new event).
String? get selectedDeviceId;

/// A list of the devices that are available to select.
List<VsCodeDevice> get devices;

static const jsonSelectedDeviceIdField = 'selectedDeviceId';
static const jsonDevicesField = 'devices';
}

/// This class defines a debug session event sent by the Dart/Flutter extensions
/// in VS Code (and must match the implementation there).
///
/// All changes to this file should be backwards-compatible and use
/// [VsCodeCapabilities] to advertise which capabilities are available and
/// handle any changes in behaviour.
abstract interface class VsCodeDebugSessionsEvent {
/// A list of debug sessions that are currently active in VS Code.
List<VsCodeDebugSession> get sessions;

static const jsonSessionsField = 'sessions';
}

/// This class defines the capabilities provided by the current version of the
/// Dart/Flutter extensions in VS Code.
///
/// All changes to this file should be backwards-compatible and use
/// [VsCodeCapabilities] to advertise which capabilities are available and
/// handle any changes in behaviour.
abstract interface class VsCodeCapabilities {
/// Whether the `executeCommand` method is available to call to execute VS
/// Code commands.
bool get executeCommand;

/// Whether the `selectDevice` method is available to call to change the
/// selected Flutter device.
bool get selectDevice;

/// Whether the `openDevToolsPage` method is available call to open a specific
/// DevTools page.
bool get openDevToolsPage;

static const jsonExecuteCommandField = 'executeCommand';
static const jsonSelectDeviceField = 'selectDevice';
static const openDevToolsPageField = 'openDevToolsPage';
}
Loading

0 comments on commit d4908fe

Please sign in to comment.