Skip to content
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

Load extensions from DevTools server instead of using stubs. #6097

Merged
merged 5 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'dart:html' as html;
import 'dart:ui' as ui;

import 'package:devtools_extensions/api.dart';
import 'package:path/path.dart' as path;

import 'controller.dart';

Expand All @@ -32,8 +33,12 @@ class EmbeddedExtensionControllerImpl extends EmbeddedExtensionController {
late final viewId = 'ext-${extensionConfig.name}-${_viewIdIncrementer++}';

String get extensionUrl {
// TODO(kenz): load the extension url being served by devtools server.
return 'https://flutter.dev/';
return path.join(
html.window.location.origin,
'devtools_extensions',
extensionConfig.name,
'index.html',
);
}

html.IFrameElement get extensionIFrame => _extensionIFrame;
Expand Down
38 changes: 29 additions & 9 deletions packages/devtools_app/lib/src/extensions/extension_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import 'package:flutter/foundation.dart';

import '../shared/config_specific/server/server.dart' as server;
import '../shared/globals.dart';
import '../shared/primitives/auto_dispose.dart';
import 'extension_model.dart';
Expand All @@ -14,17 +15,36 @@ class ExtensionService extends DisposableController
_availableExtensions;
final _availableExtensions = ValueNotifier<List<DevToolsExtensionConfig>>([]);

void initialize() {
addAutoDisposeListener(serviceManager.connectedState, () {
_refreshAvailableExtensions();
Future<void> initialize() async {
await maybeRefreshExtensions();
addAutoDisposeListener(serviceManager.connectedState, () async {
kenzieschmoll marked this conversation as resolved.
Show resolved Hide resolved
await maybeRefreshExtensions();
});

// TODO(kenz): we should also refresh the available extensions on some event
// from the analysis server that is watching the
// .dart_tool/package_config.json file for changes.
}

Future<void> maybeRefreshExtensions() async {
final appRootPath = await _connectedAppRootPath();
if (appRootPath != null) {
await _refreshAvailableExtensions(appRootPath);
}
}

// TODO(kenz): actually look up the available extensions from devtools server,
// based on the root path(s) from the available isolate(s).
int _count = 0;
void _refreshAvailableExtensions() {
_availableExtensions.value =
debugExtensions.sublist(0, _count++ % (debugExtensions.length + 1));
Future<void> _refreshAvailableExtensions(String? rootPath) async {
final extensions = await server.refreshAvailableExtensions(rootPath);
kenzieschmoll marked this conversation as resolved.
Show resolved Hide resolved
_availableExtensions.value = extensions;
}
}

Future<String?> _connectedAppRootPath() async {
var fileUri = await serviceManager.rootLibraryForSelectedIsolate();
if (fileUri == null) return null;

if (fileUri.endsWith('/lib/main.dart')) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if the root library isn't main.dart? There's no requirement for the entry point of a Dart or Flutter program to be in main.dart. I'm not even sure there's a requirement for it to be in a library of the form lib/<script> for Flutter, but there definitely isn't for Dart programs.

Copy link
Member Author

@kenzieschmoll kenzieschmoll Jul 26, 2023

Choose a reason for hiding this comment

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

Is there a convention for finding the root directory that contains the .dart_tool folder? Is this guaranteed to always be the parent of lib/?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure, especially if you don't have access to the FS. If you did, you'd just walk the directory chain until you found the directory containing .dart_tool.

I think you're probably safe to assume that the entrypoint is under lib/, so if you update this to fileUri.endsWith(RegExp(r'\/lib\/[^\/.]*.dart')) this should work the vast majority of the time.

Copy link
Member Author

Choose a reason for hiding this comment

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

Optionally, I could just pass the whole root library to the server and have the server look for the .dart_tool folder?

Copy link
Contributor

Choose a reason for hiding this comment

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

That'd be the most accurate way of handling it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Used the regexp for now but added a todo to move this logic to the server.

fileUri = fileUri.replaceFirst('/lib/main.dart', '');
}
return fileUri;
}
19 changes: 18 additions & 1 deletion packages/devtools_app/lib/src/service/service_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ class ServiceConnectionManager {
}

if (FeatureFlags.devToolsExtensions) {
extensionService.initialize();
await extensionService.initialize();
}

_connectedState.value = const ConnectedState(true);
Expand Down Expand Up @@ -539,6 +539,23 @@ class ServiceConnectionManager {
await whenValueNonNull(isolateManager.mainIsolate);
return libraryUriAvailableNow(uri);
}

Future<String?> rootLibraryForSelectedIsolate() async {
if (!connectedState.value.connected) return null;

final selectedIsolateRef = isolateManager.mainIsolate.value?.id;
kenzieschmoll marked this conversation as resolved.
Show resolved Hide resolved
if (selectedIsolateRef == null) return null;

final selectedIsolate = await service!.getIsolate(selectedIsolateRef);
kenzieschmoll marked this conversation as resolved.
Show resolved Hide resolved
final rootLib = selectedIsolate.rootLib?.uri;
if (rootLib == null) return null;

await resolvedUriManager.fetchFileUris(selectedIsolateRef, [rootLib]);
return resolvedUriManager.lookupFileUri(
selectedIsolateRef,
rootLib,
);
}
}

class VmServiceCapabilities {
Expand Down
Loading