Unverified Commit f5de6aad authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] remove zone level overrides of verbose and daemon logging (#57448)

Make it possible for all FlutterCommands to be global free, by moving instantiation to inside the Zone context. Additionally, provide VerboseLogger and NotifyLogger (daemon) at the top level and remove from command-specific overrides.

This allows removing a work around where web devices needed to look up directly from the context in non-test code.

Technically the output preferences are still zone injected, but these will be moved soon as they were not being used correctly by the top level command (the injection comes after ArgParser reads the overflow values, causing numerous wrap issues)
parent e9a47599
...@@ -98,7 +98,7 @@ Future<void> main(List<String> args) async { ...@@ -98,7 +98,7 @@ Future<void> main(List<String> args) async {
Cache.disableLocking(); // ignore: invalid_use_of_visible_for_testing_member Cache.disableLocking(); // ignore: invalid_use_of_visible_for_testing_member
await runner.run( await runner.run(
command, command,
<FlutterCommand>[ () => <FlutterCommand>[
_FuchsiaAttachCommand(), _FuchsiaAttachCommand(),
_FuchsiaDoctorCommand(), // If attach fails the tool will attempt to run doctor. _FuchsiaDoctorCommand(), // If attach fails the tool will attempt to run doctor.
], ],
......
...@@ -6,6 +6,7 @@ import 'dart:async'; ...@@ -6,6 +6,7 @@ import 'dart:async';
import 'runner.dart' as runner; import 'runner.dart' as runner;
import 'src/base/context.dart'; import 'src/base/context.dart';
import 'src/base/logger.dart';
import 'src/base/template.dart'; import 'src/base/template.dart';
// The build_runner code generation is provided here to make it easier to // The build_runner code generation is provided here to make it easier to
// avoid introducing the dependency into google3. Not all build* packages // avoid introducing the dependency into google3. Not all build* packages
...@@ -64,8 +65,9 @@ Future<void> main(List<String> args) async { ...@@ -64,8 +65,9 @@ Future<void> main(List<String> args) async {
(args.isNotEmpty && args.first == 'help') || (args.length == 1 && verbose); (args.isNotEmpty && args.first == 'help') || (args.length == 1 && verbose);
final bool muteCommandLogging = help || doctor; final bool muteCommandLogging = help || doctor;
final bool verboseHelp = help && verbose; final bool verboseHelp = help && verbose;
final bool daemon = args.contains('daemon');
await runner.run(args, <FlutterCommand>[ await runner.run(args, () => <FlutterCommand>[
AnalyzeCommand( AnalyzeCommand(
verboseHelp: verboseHelp, verboseHelp: verboseHelp,
fileSystem: globals.fs, fileSystem: globals.fs,
...@@ -122,5 +124,14 @@ Future<void> main(List<String> args) async { ...@@ -122,5 +124,14 @@ Future<void> main(List<String> args) async {
WebRunnerFactory: () => DwdsWebRunnerFactory(), WebRunnerFactory: () => DwdsWebRunnerFactory(),
// The mustache dependency is different in google3 // The mustache dependency is different in google3
TemplateRenderer: () => const MustacheTemplateRenderer(), TemplateRenderer: () => const MustacheTemplateRenderer(),
if (daemon)
Logger: () => NotifyingLogger(verbose: verbose)
else if (verbose)
Logger: () => VerboseLogger(StdoutLogger(
timeoutConfiguration: timeoutConfiguration,
stdio: globals.stdio,
terminal: globals.terminal,
outputPreferences: globals.outputPreferences,
))
}); });
} }
...@@ -23,9 +23,12 @@ import 'src/runner/flutter_command.dart'; ...@@ -23,9 +23,12 @@ import 'src/runner/flutter_command.dart';
import 'src/runner/flutter_command_runner.dart'; import 'src/runner/flutter_command_runner.dart';
/// Runs the Flutter tool with support for the specified list of [commands]. /// Runs the Flutter tool with support for the specified list of [commands].
///
/// [commands] must be either `List<FlutterCommand>` or `List<FlutterCommand> Function()`.
// TODO(jonahwilliams): update command type once g3 has rolled.
Future<int> run( Future<int> run(
List<String> args, List<String> args,
List<FlutterCommand> commands, { dynamic commands, {
bool muteCommandLogging = false, bool muteCommandLogging = false,
bool verbose = false, bool verbose = false,
bool verboseHelp = false, bool verboseHelp = false,
...@@ -39,12 +42,17 @@ Future<int> run( ...@@ -39,12 +42,17 @@ Future<int> run(
args = List<String>.of(args); args = List<String>.of(args);
args.removeWhere((String option) => option == '-v' || option == '--verbose'); args.removeWhere((String option) => option == '-v' || option == '--verbose');
} }
List<FlutterCommand> Function() commandGenerator;
final FlutterCommandRunner runner = FlutterCommandRunner(verboseHelp: verboseHelp); if (commands is List<FlutterCommand>) {
commands.forEach(runner.addCommand); commandGenerator = () => commands;
} else {
commandGenerator = commands as List<FlutterCommand> Function();
}
return runInContext<int>(() async { return runInContext<int>(() async {
reportCrashes ??= !await globals.isRunningOnBot; reportCrashes ??= !await globals.isRunningOnBot;
final FlutterCommandRunner runner = FlutterCommandRunner(verboseHelp: verboseHelp);
commandGenerator().forEach(runner.addCommand);
// Initialize the system locale. // Initialize the system locale.
final String systemLocale = await intl_standalone.findSystemLocale(); final String systemLocale = await intl_standalone.findSystemLocale();
......
...@@ -11,7 +11,6 @@ import '../base/file_system.dart'; ...@@ -11,7 +11,6 @@ import '../base/file_system.dart';
import '../base/logger.dart'; import '../base/logger.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../base/terminal.dart'; import '../base/terminal.dart';
import '../globals.dart' as globals;
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
import 'analyze_continuously.dart'; import 'analyze_continuously.dart';
import 'analyze_once.dart'; import 'analyze_once.dart';
...@@ -113,9 +112,7 @@ class AnalyzeCommand extends FlutterCommand { ...@@ -113,9 +112,7 @@ class AnalyzeCommand extends FlutterCommand {
runner.getRepoRoots(), runner.getRepoRoots(),
runner.getRepoPackages(), runner.getRepoPackages(),
fileSystem: _fileSystem, fileSystem: _fileSystem,
// TODO(jonahwilliams): determine a better way to inject the logger, logger: _logger,
// since it is constructed on-demand.
logger: _logger ?? globals.logger,
platform: _platform, platform: _platform,
processManager: _processManager, processManager: _processManager,
terminal: _terminal, terminal: _terminal,
...@@ -128,9 +125,7 @@ class AnalyzeCommand extends FlutterCommand { ...@@ -128,9 +125,7 @@ class AnalyzeCommand extends FlutterCommand {
runner.getRepoPackages(), runner.getRepoPackages(),
workingDirectory: workingDirectory, workingDirectory: workingDirectory,
fileSystem: _fileSystem, fileSystem: _fileSystem,
// TODO(jonahwilliams): determine a better way to inject the logger, logger: _logger,
// since it is constructed on-demand.
logger: _logger ?? globals.logger,
platform: _platform, platform: _platform,
processManager: _processManager, processManager: _processManager,
terminal: _terminal, terminal: _terminal,
......
...@@ -200,7 +200,7 @@ class AttachCommand extends FlutterCommand { ...@@ -200,7 +200,7 @@ class AttachCommand extends FlutterCommand {
? Daemon( ? Daemon(
stdinCommandStream, stdinCommandStream,
stdoutCommandResponse, stdoutCommandResponse,
notifyingLogger: NotifyingLogger(), notifyingLogger: NotifyingLogger(verbose: globals.logger.isVerbose),
logToStdout: true, logToStdout: true,
) )
: null; : null;
......
...@@ -52,27 +52,16 @@ class DaemonCommand extends FlutterCommand { ...@@ -52,27 +52,16 @@ class DaemonCommand extends FlutterCommand {
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
globals.printStatus('Starting device daemon...'); globals.printStatus('Starting device daemon...');
isRunningFromDaemon = true; isRunningFromDaemon = true;
final NotifyingLogger notifyingLogger = NotifyingLogger();
Cache.releaseLockEarly(); Cache.releaseLockEarly();
await context.run<void>(
body: () async {
final Daemon daemon = Daemon( final Daemon daemon = Daemon(
stdinCommandStream, stdoutCommandResponse, stdinCommandStream, stdoutCommandResponse,
notifyingLogger: notifyingLogger, notifyingLogger: globals.logger as NotifyingLogger,
); );
final int code = await daemon.onExit; final int code = await daemon.onExit;
if (code != 0) { if (code != 0) {
throwToolExit('Daemon exited with non-zero exit code: $code', exitCode: code); throwToolExit('Daemon exited with non-zero exit code: $code', exitCode: code);
} }
},
overrides: <Type, Generator>{
Logger: () => notifyingLogger,
},
);
return FlutterCommandResult.success(); return FlutterCommandResult.success();
} }
} }
...@@ -927,7 +916,22 @@ dynamic _toJsonable(dynamic obj) { ...@@ -927,7 +916,22 @@ dynamic _toJsonable(dynamic obj) {
} }
class NotifyingLogger extends Logger { class NotifyingLogger extends Logger {
final StreamController<LogMessage> _messageController = StreamController<LogMessage>.broadcast(); NotifyingLogger({ @required this.verbose }) {
_messageController = StreamController<LogMessage>.broadcast(
onListen: _onListen,
);
}
final bool verbose;
final List<LogMessage> messageBuffer = <LogMessage>[];
StreamController<LogMessage> _messageController;
void _onListen() {
if (messageBuffer.isNotEmpty) {
messageBuffer.forEach(_messageController.add);
messageBuffer.clear();
}
}
Stream<LogMessage> get onMessage => _messageController.stream; Stream<LogMessage> get onMessage => _messageController.stream;
...@@ -941,7 +945,7 @@ class NotifyingLogger extends Logger { ...@@ -941,7 +945,7 @@ class NotifyingLogger extends Logger {
int hangingIndent, int hangingIndent,
bool wrap, bool wrap,
}) { }) {
_messageController.add(LogMessage('error', message, stackTrace)); _sendMessage(LogMessage('error', message, stackTrace));
} }
@override @override
...@@ -954,12 +958,15 @@ class NotifyingLogger extends Logger { ...@@ -954,12 +958,15 @@ class NotifyingLogger extends Logger {
int hangingIndent, int hangingIndent,
bool wrap, bool wrap,
}) { }) {
_messageController.add(LogMessage('status', message)); _sendMessage(LogMessage('status', message));
} }
@override @override
void printTrace(String message) { void printTrace(String message) {
// This is a lot of traffic to send over the wire. if (!verbose) {
return;
}
_sendMessage(LogMessage('trace', message));
} }
@override @override
...@@ -979,6 +986,13 @@ class NotifyingLogger extends Logger { ...@@ -979,6 +986,13 @@ class NotifyingLogger extends Logger {
); );
} }
void _sendMessage(LogMessage logMessage) {
if (_messageController.hasListener) {
return _messageController.add(logMessage);
}
messageBuffer.add(logMessage);
}
void dispose() { void dispose() {
_messageController.close(); _messageController.close();
} }
......
...@@ -407,7 +407,7 @@ class RunCommand extends RunCommandBase { ...@@ -407,7 +407,7 @@ class RunCommand extends RunCommandBase {
final Daemon daemon = Daemon( final Daemon daemon = Daemon(
stdinCommandStream, stdinCommandStream,
stdoutCommandResponse, stdoutCommandResponse,
notifyingLogger: NotifyingLogger(), notifyingLogger: NotifyingLogger(verbose: globals.logger.isVerbose),
logToStdout: true, logToStdout: true,
); );
AppInstance app; AppInstance app;
......
...@@ -103,6 +103,7 @@ class DeviceManager { ...@@ -103,6 +103,7 @@ class DeviceManager {
fileSystem: globals.fs, fileSystem: globals.fs,
platform: globals.platform, platform: globals.platform,
processManager: globals.processManager, processManager: globals.processManager,
logger: globals.logger,
), ),
]); ]);
......
...@@ -15,7 +15,6 @@ import '../artifacts.dart'; ...@@ -15,7 +15,6 @@ import '../artifacts.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../base/context.dart'; import '../base/context.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/terminal.dart'; import '../base/terminal.dart';
import '../base/user_messages.dart'; import '../base/user_messages.dart';
import '../base/utils.dart'; import '../base/utils.dart';
...@@ -236,12 +235,6 @@ class FlutterCommandRunner extends CommandRunner<void> { ...@@ -236,12 +235,6 @@ class FlutterCommandRunner extends CommandRunner<void> {
Future<void> runCommand(ArgResults topLevelResults) async { Future<void> runCommand(ArgResults topLevelResults) async {
final Map<Type, dynamic> contextOverrides = <Type, dynamic>{}; final Map<Type, dynamic> contextOverrides = <Type, dynamic>{};
// Check for verbose.
if (topLevelResults['verbose'] as bool) {
// Override the logger.
contextOverrides[Logger] = VerboseLogger(globals.logger);
}
// Don't set wrapColumns unless the user said to: if it's set, then all // Don't set wrapColumns unless the user said to: if it's set, then all
// wrapping will occur at this width explicitly, and won't adapt if the // wrapping will occur at this width explicitly, and won't adapt if the
// terminal size changes during a run. // terminal size changes during a run.
......
...@@ -14,7 +14,6 @@ import '../base/platform.dart'; ...@@ -14,7 +14,6 @@ import '../base/platform.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../device.dart'; import '../device.dart';
import '../features.dart'; import '../features.dart';
import '../globals.dart' as globals;
import '../project.dart'; import '../project.dart';
import 'chrome.dart'; import 'chrome.dart';
...@@ -36,7 +35,7 @@ abstract class ChromiumDevice extends Device { ...@@ -36,7 +35,7 @@ abstract class ChromiumDevice extends Device {
@required String name, @required String name,
@required this.chromeLauncher, @required this.chromeLauncher,
@required FileSystem fileSystem, @required FileSystem fileSystem,
Logger logger, @required Logger logger,
}) : _fileSystem = fileSystem, }) : _fileSystem = fileSystem,
_logger = logger, _logger = logger,
super( super(
...@@ -49,7 +48,7 @@ abstract class ChromiumDevice extends Device { ...@@ -49,7 +48,7 @@ abstract class ChromiumDevice extends Device {
final ChromiumLauncher chromeLauncher; final ChromiumLauncher chromeLauncher;
final FileSystem _fileSystem; final FileSystem _fileSystem;
Logger _logger; final Logger _logger;
/// The active chrome instance. /// The active chrome instance.
Chromium _chrome; Chromium _chrome;
...@@ -115,7 +114,6 @@ abstract class ChromiumDevice extends Device { ...@@ -115,7 +114,6 @@ abstract class ChromiumDevice extends Device {
bool prebuiltApplication = false, bool prebuiltApplication = false,
bool ipv6 = false, bool ipv6 = false,
}) async { }) async {
_logger ??= globals.logger;
// See [ResidentWebRunner.run] in flutter_tools/lib/src/resident_web_runner.dart // See [ResidentWebRunner.run] in flutter_tools/lib/src/resident_web_runner.dart
// for the web initialization and server logic. // for the web initialization and server logic.
final String url = platformArgs['uri'] as String; final String url = platformArgs['uri'] as String;
...@@ -241,10 +239,7 @@ class MicrosoftEdgeDevice extends ChromiumDevice { ...@@ -241,10 +239,7 @@ class MicrosoftEdgeDevice extends ChromiumDevice {
class WebDevices extends PollingDeviceDiscovery { class WebDevices extends PollingDeviceDiscovery {
WebDevices({ WebDevices({
@required FileSystem fileSystem, @required FileSystem fileSystem,
// TODO(jonahwilliams): the logger is overriden by the daemon command @required Logger logger,
// to support IDE integration. Accessing the global logger too early
// will grab the old stdout logger.
Logger logger,
@required Platform platform, @required Platform platform,
@required ProcessManager processManager, @required ProcessManager processManager,
@required FeatureFlags featureFlags, @required FeatureFlags featureFlags,
...@@ -307,7 +302,7 @@ String parseVersionForWindows(String input) { ...@@ -307,7 +302,7 @@ String parseVersionForWindows(String input) {
/// A special device type to allow serving for arbitrary browsers. /// A special device type to allow serving for arbitrary browsers.
class WebServerDevice extends Device { class WebServerDevice extends Device {
WebServerDevice({ WebServerDevice({
Logger logger, @required Logger logger,
}) : _logger = logger, }) : _logger = logger,
super( super(
'web-server', 'web-server',
...@@ -316,7 +311,7 @@ class WebServerDevice extends Device { ...@@ -316,7 +311,7 @@ class WebServerDevice extends Device {
ephemeral: false, ephemeral: false,
); );
Logger _logger; final Logger _logger;
@override @override
void clearLogs() { } void clearLogs() { }
...@@ -375,7 +370,6 @@ class WebServerDevice extends Device { ...@@ -375,7 +370,6 @@ class WebServerDevice extends Device {
bool prebuiltApplication = false, bool prebuiltApplication = false,
bool ipv6 = false, bool ipv6 = false,
}) async { }) async {
_logger ??= globals.logger;
final String url = platformArgs['uri'] as String; final String url = platformArgs['uri'] as String;
if (debuggingOptions.startPaused) { if (debuggingOptions.startPaused) {
_logger.printStatus('Waiting for connection from Dart debug extension at $url', emphasis: true); _logger.printStatus('Waiting for connection from Dart debug extension at $url', emphasis: true);
......
...@@ -24,7 +24,7 @@ void main() { ...@@ -24,7 +24,7 @@ void main() {
group('daemon', () { group('daemon', () {
setUp(() { setUp(() {
notifyingLogger = NotifyingLogger(); notifyingLogger = NotifyingLogger(verbose: false);
}); });
tearDown(() { tearDown(() {
...@@ -298,6 +298,42 @@ void main() { ...@@ -298,6 +298,42 @@ void main() {
}); });
}); });
testUsingContext('notifyingLogger outputs trace messages in verbose mode', () async {
final NotifyingLogger logger = NotifyingLogger(verbose: true);
final Future<LogMessage> messageResult = logger.onMessage.first;
logger.printTrace('test');
final LogMessage message = await messageResult;
expect(message.level, 'trace');
expect(message.message, 'test');
});
testUsingContext('notifyingLogger ignores trace messages in non-verbose mode', () async {
final NotifyingLogger logger = NotifyingLogger(verbose: false);
final Future<LogMessage> messageResult = logger.onMessage.first;
logger.printTrace('test');
logger.printStatus('hello');
final LogMessage message = await messageResult;
expect(message.level, 'status');
expect(message.message, 'hello');
});
testUsingContext('notifyingLogger buffers messages sent before a subscription', () async {
final NotifyingLogger logger = NotifyingLogger(verbose: false);
logger.printStatus('hello');
final LogMessage message = await logger.onMessage.first;
expect(message.level, 'status');
expect(message.message, 'hello');
});
group('daemon serialization', () { group('daemon serialization', () {
test('OperationResult', () { test('OperationResult', () {
expect( expect(
......
...@@ -44,7 +44,7 @@ void main() { ...@@ -44,7 +44,7 @@ void main() {
() { () {
unawaited(runner.run( unawaited(runner.run(
<String>['test'], <String>['test'],
<FlutterCommand>[ () => <FlutterCommand>[
CrashingFlutterCommand(), CrashingFlutterCommand(),
], ],
// This flutterVersion disables crash reporting. // This flutterVersion disables crash reporting.
...@@ -86,7 +86,7 @@ void main() { ...@@ -86,7 +86,7 @@ void main() {
() { () {
unawaited(runner.run( unawaited(runner.run(
<String>['test'], <String>['test'],
<FlutterCommand>[ () => <FlutterCommand>[
CrashingFlutterCommand(), CrashingFlutterCommand(),
], ],
// This flutterVersion disables crash reporting. // This flutterVersion disables crash reporting.
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/web/chrome.dart'; import 'package:flutter_tools/src/web/chrome.dart';
import 'package:flutter_tools/src/web/web_device.dart'; import 'package:flutter_tools/src/web/web_device.dart';
...@@ -16,37 +15,6 @@ import '../../src/context.dart'; ...@@ -16,37 +15,6 @@ import '../../src/context.dart';
import '../../src/testbed.dart'; import '../../src/testbed.dart';
void main() { void main() {
testUsingContext('Grabs context logger if no constructor logger is provided', () async {
final WebDevices webDevices = WebDevices(
featureFlags: TestFeatureFlags(isWebEnabled: true),
fileSystem: MemoryFileSystem.test(),
platform: FakePlatform(
operatingSystem: 'linux',
environment: <String, String>{}
),
processManager: FakeProcessManager.any(),
);
final List<Device> devices = await webDevices.pollingGetDevices();
final WebServerDevice serverDevice = devices.firstWhere((Device device) => device is WebServerDevice)
as WebServerDevice;
await serverDevice.startApp(
null,
debuggingOptions: DebuggingOptions.enabled(
BuildInfo.debug,
startPaused: true,
),
platformArgs: <String, String>{
'uri': 'foo',
}
);
// Verify that the injected testLogger is used.
expect(testLogger.statusText, contains(
'Waiting for connection from Dart debug extension at foo'
));
});
testWithoutContext('No web devices listed if feature is disabled', () async { testWithoutContext('No web devices listed if feature is disabled', () async {
final WebDevices webDevices = WebDevices( final WebDevices webDevices = WebDevices(
featureFlags: TestFeatureFlags(isWebEnabled: false), featureFlags: TestFeatureFlags(isWebEnabled: false),
......
...@@ -47,7 +47,7 @@ void main() { ...@@ -47,7 +47,7 @@ void main() {
'id': 1, 'id': 1,
'method': 'device.enable', 'method': 'device.enable',
})}]'); })}]');
response = await stream.first; response = await stream.firstWhere((Map<String, Object> json) => json['id'] == 1);
expect(response['id'], 1); expect(response['id'], 1);
expect(response['error'], isNull); expect(response['error'], isNull);
......
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
import 'dart:async'; import 'dart:async';
import 'package:args/args.dart';
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/convert.dart'; import 'package:flutter_tools/src/convert.dart';
import 'package:vm_service/vm_service.dart' as vm_service; import 'package:vm_service/vm_service.dart' as vm_service;
...@@ -78,7 +80,7 @@ String getFlutterRoot() { ...@@ -78,7 +80,7 @@ String getFlutterRoot() {
} }
CommandRunner<void> createTestCommandRunner([ FlutterCommand command ]) { CommandRunner<void> createTestCommandRunner([ FlutterCommand command ]) {
final FlutterCommandRunner runner = FlutterCommandRunner(); final FlutterCommandRunner runner = TestFlutterCommandRunner();
if (command != null) { if (command != null) {
runner.addCommand(command); runner.addCommand(command);
} }
...@@ -352,3 +354,20 @@ class FakeVmServiceStreamResponse implements VmServiceExpectation { ...@@ -352,3 +354,20 @@ class FakeVmServiceStreamResponse implements VmServiceExpectation {
@override @override
bool get isRequest => false; bool get isRequest => false;
} }
class TestFlutterCommandRunner extends FlutterCommandRunner {
@override
Future<void> runCommand(ArgResults topLevelResults) async {
final Logger topLevelLogger = globals.logger;
final Map<Type, dynamic> contextOverrides = <Type, dynamic>{
if (topLevelResults['verbose'] as bool)
Logger: VerboseLogger(topLevelLogger),
};
return context.run<void>(
overrides: contextOverrides.map<Type, Generator>((Type type, dynamic value) {
return MapEntry<Type, Generator>(type, () => value);
}),
body: () => super.runCommand(topLevelResults),
);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment