Skip to content

Commit 9fd9338

Browse files
authored
DTDManager handles the subscription to the service stream (#9635)
1 parent 2225e7f commit 9fd9338

7 files changed

Lines changed: 378 additions & 77 deletions

File tree

packages/devtools_app/lib/src/shared/editor/editor_client.dart

Lines changed: 86 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,85 +4,54 @@
44

55
import 'dart:async';
66

7+
import 'package:devtools_app_shared/service.dart';
78
import 'package:devtools_app_shared/utils.dart';
89
import 'package:devtools_shared/devtools_shared.dart';
910
import 'package:dtd/dtd.dart';
1011
import 'package:flutter/foundation.dart';
1112
import 'package:json_rpc_2/json_rpc_2.dart';
13+
import 'package:logging/logging.dart';
1214

1315
import '../analytics/constants.dart';
1416
import '../framework/app_error_handling.dart';
1517
import 'api_classes.dart';
1618

19+
final _log = Logger('editor_client');
20+
1721
/// A client wrapper that connects to an editor over DTD.
1822
///
1923
/// Changes made to the editor services/events should be considered carefully to
2024
/// ensure they are not breaking changes to already-shipped editors.
2125
class EditorClient extends DisposableController
2226
with AutoDisposeControllerMixin {
23-
EditorClient(this._dtd) {
27+
EditorClient(this._dtdManager) {
2428
unawaited(initialized); // Trigger async initialization.
2529
}
2630

27-
final DartToolingDaemon _dtd;
31+
final DTDManager _dtdManager;
2832
late final initialized = _initialize();
2933

34+
DartToolingDaemon get _dtd => _dtdManager.connection.value!;
35+
3036
String get gaId => EditorSidebar.id;
3137

3238
Future<void> _initialize() async {
3339
autoDisposeStreamSubscription(
34-
_dtd.onEvent(CoreDtdServiceConstants.servicesStreamId).listen((data) {
35-
final kind = data.kind;
36-
if (kind != CoreDtdServiceConstants.serviceRegisteredKind &&
37-
kind != CoreDtdServiceConstants.serviceUnregisteredKind) {
38-
return;
39-
}
40-
40+
_dtdManager.serviceRegistrationBroadcastStream.listen((data) {
4141
final service = data.data[DtdParameters.service] as String?;
42-
if (service == null ||
43-
(service != editorServiceName && service != lspServiceName)) {
44-
return;
45-
}
46-
42+
if (service == null) return;
4743
final isRegistered =
48-
kind == CoreDtdServiceConstants.serviceRegisteredKind;
44+
data.kind == CoreDtdServiceConstants.serviceRegisteredKind;
4945
final method = data.data[DtdParameters.method] as String;
5046
final capabilities =
5147
data.data[DtdParameters.capabilities] as Map<String, Object?>?;
52-
final lspMethod = LspMethod.fromMethodName(method);
53-
if (lspMethod != null) {
54-
lspMethod.isRegistered = isRegistered;
55-
if (lspMethod == LspMethod.editableArguments) {
56-
// Update the notifier so that the Property Editor is aware that the
57-
// editableArguments API is registered.
58-
_editableArgumentsApiIsRegistered.value = isRegistered;
59-
}
60-
} else if (method == EditorMethod.getDevices.name) {
61-
_supportsGetDevices = isRegistered;
62-
} else if (method == EditorMethod.getDebugSessions.name) {
63-
_supportsGetDebugSessions = isRegistered;
64-
} else if (method == EditorMethod.selectDevice.name) {
65-
_supportsSelectDevice = isRegistered;
66-
} else if (method == EditorMethod.hotReload.name) {
67-
_supportsHotReload = isRegistered;
68-
} else if (method == EditorMethod.hotRestart.name) {
69-
_supportsHotRestart = isRegistered;
70-
} else if (method == EditorMethod.openDevToolsPage.name) {
71-
_supportsOpenDevToolsPage = isRegistered;
72-
_supportsOpenDevToolsForceExternal =
73-
capabilities?[Field.supportsForceExternal] == true;
74-
} else {
75-
return;
76-
}
7748

78-
final info = isRegistered
79-
? ServiceRegistered(
80-
service: service,
81-
method: method,
82-
capabilities: capabilities,
83-
)
84-
: ServiceUnregistered(service: service, method: method);
85-
_editorServiceChangedController.add(info);
49+
_handleServiceRegistration(
50+
service: service,
51+
method: method,
52+
capabilities: capabilities,
53+
isRegistered: isRegistered,
54+
);
8655
}),
8756
);
8857

@@ -126,15 +95,75 @@ class EditorClient extends DisposableController
12695
}
12796
}),
12897
);
129-
await [
130-
_dtd.streamListen(CoreDtdServiceConstants.servicesStreamId),
131-
_dtd.streamListen(editorStreamName).catchError((_) {
132-
// Because we currently call streamListen in two places (here and
133-
// ThemeManager) this can fail. It doesn't matter if this happens,
134-
// however we should refactor this code to better support using the DTD
135-
// connection in multiple places without them having to coordinate.
136-
}),
137-
].wait;
98+
99+
await _dtd.streamListen(editorStreamName).catchError((_) {
100+
// Because we currently call streamListen in two places (here and
101+
// ThemeManager) this can fail. It doesn't matter if this happens,
102+
// however we should refactor this code to better support using the DTD
103+
// connection in multiple places without them having to coordinate.
104+
});
105+
106+
// Check if any client services have already been registered against DTD.
107+
try {
108+
final response = await _dtd.getRegisteredServices();
109+
for (final service in response.clientServices) {
110+
for (final method in service.methods.values) {
111+
_handleServiceRegistration(
112+
service: service.name,
113+
method: method.name,
114+
capabilities: method.capabilities,
115+
);
116+
}
117+
}
118+
} catch (e) {
119+
_log.warning('Failed to fetch registered services: $e');
120+
}
121+
}
122+
123+
void _handleServiceRegistration({
124+
required String service,
125+
required String method,
126+
Map<String, Object?>? capabilities,
127+
bool isRegistered = true,
128+
}) {
129+
if (service != editorServiceName && service != lspServiceName) {
130+
return;
131+
}
132+
133+
final lspMethod = LspMethod.fromMethodName(method);
134+
if (lspMethod != null) {
135+
lspMethod.isRegistered = isRegistered;
136+
if (lspMethod == LspMethod.editableArguments) {
137+
// Update the notifier so that the Property Editor is aware that the
138+
// editableArguments API is registered.
139+
_editableArgumentsApiIsRegistered.value = isRegistered;
140+
}
141+
} else if (method == EditorMethod.getDevices.name) {
142+
_supportsGetDevices = isRegistered;
143+
} else if (method == EditorMethod.getDebugSessions.name) {
144+
_supportsGetDebugSessions = isRegistered;
145+
} else if (method == EditorMethod.selectDevice.name) {
146+
_supportsSelectDevice = isRegistered;
147+
} else if (method == EditorMethod.hotReload.name) {
148+
_supportsHotReload = isRegistered;
149+
} else if (method == EditorMethod.hotRestart.name) {
150+
_supportsHotRestart = isRegistered;
151+
} else if (method == EditorMethod.openDevToolsPage.name) {
152+
_supportsOpenDevToolsPage = isRegistered;
153+
_supportsOpenDevToolsForceExternal =
154+
capabilities?[Field.supportsForceExternal] == true;
155+
} else {
156+
return;
157+
}
158+
159+
final info = isRegistered
160+
? ServiceRegistered(
161+
service: service,
162+
method: method,
163+
capabilities: capabilities,
164+
)
165+
: ServiceUnregistered(service: service, method: method);
166+
_editorServiceChangedController.add(info);
138167
}
139168

140169
/// Close the connection to DTD.

packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_panel.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
import 'dart:async';
66

7+
import 'package:devtools_app_shared/service.dart';
78
import 'package:devtools_app_shared/ui.dart';
89
import 'package:devtools_app_shared/utils.dart';
9-
import 'package:dtd/dtd.dart';
1010
import 'package:flutter/material.dart';
1111

1212
import '../../../framework/scaffold/report_feedback_button.dart';
@@ -20,9 +20,9 @@ import 'property_editor_view.dart';
2020

2121
/// The side panel for the Property Editor.
2222
class PropertyEditorPanel extends StatefulWidget {
23-
const PropertyEditorPanel(this.dtd, {super.key});
23+
const PropertyEditorPanel(this.dtdManager, {super.key});
2424

25-
final DartToolingDaemon dtd;
25+
final DTDManager dtdManager;
2626

2727
@override
2828
State<PropertyEditorPanel> createState() => _PropertyEditorPanelState();
@@ -38,7 +38,7 @@ class _PropertyEditorPanelState extends State<PropertyEditorPanel> {
3838
void initState() {
3939
super.initState();
4040

41-
final editor = EditorClient(widget.dtd);
41+
final editor = EditorClient(widget.dtdManager);
4242
ga.screen(gac.PropertyEditorSidebar.id);
4343
unawaited(
4444
_editor = editor.initialized.then((_) {

packages/devtools_app/lib/src/standalone_ui/standalone_screen.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ enum StandaloneScreenType {
3737
),
3838
StandaloneScreenType.editorSidebar => _DtdConnectedScreen(
3939
dtdManager: dtdManager,
40-
builder: (dtd) => EditorSidebarPanel(dtd),
40+
builder: EditorSidebarPanel.new,
4141
),
4242
StandaloneScreenType.propertyEditor => _DtdConnectedScreen(
4343
dtdManager: dtdManager,
44-
builder: (dtd) => PropertyEditorPanel(dtd),
44+
builder: PropertyEditorPanel.new,
4545
),
4646
};
4747
}
@@ -58,7 +58,7 @@ class _DtdConnectedScreen extends StatelessWidget {
5858
const _DtdConnectedScreen({required this.dtdManager, required this.builder});
5959

6060
final DTDManager dtdManager;
61-
final Widget Function(DartToolingDaemon) builder;
61+
final Widget Function(DTDManager) builder;
6262

6363
@override
6464
Widget build(BuildContext context) {
@@ -80,7 +80,7 @@ class _DtdConnectedScreen extends StatelessWidget {
8080
// reconnect occurs.
8181
KeyedSubtree(
8282
key: ValueKey(connection),
83-
child: builder(connection),
83+
child: builder(dtdManager),
8484
),
8585
if (connectionState is! ConnectedDTDState)
8686
NotConnectedOverlay(connectionState),

packages/devtools_app/lib/src/standalone_ui/vs_code/flutter_panel.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
import 'dart:async';
66

7+
import 'package:devtools_app_shared/service.dart';
78
import 'package:devtools_app_shared/ui.dart';
89
import 'package:devtools_app_shared/utils.dart';
9-
import 'package:dtd/dtd.dart';
1010
import 'package:flutter/material.dart';
1111

1212
import '../../shared/analytics/analytics.dart' as ga;
@@ -22,9 +22,9 @@ import 'devtools/devtools_view.dart';
2222
/// Provides some basic functionality to improve discoverability of features
2323
/// such as creation of new projects, device selection and DevTools features.
2424
class EditorSidebarPanel extends StatefulWidget {
25-
const EditorSidebarPanel(this.dtd, {super.key});
25+
const EditorSidebarPanel(this.dtdManager, {super.key});
2626

27-
final DartToolingDaemon dtd;
27+
final DTDManager dtdManager;
2828

2929
@override
3030
State<EditorSidebarPanel> createState() => _EditorSidebarPanelState();
@@ -39,7 +39,7 @@ class _EditorSidebarPanelState extends State<EditorSidebarPanel> {
3939
void initState() {
4040
super.initState();
4141

42-
final editor = EditorClient(widget.dtd);
42+
final editor = EditorClient(widget.dtdManager);
4343
ga.screen(editor.gaId);
4444
unawaited(_editor = editor.initialized.then((_) => editor));
4545
}

0 commit comments

Comments
 (0)