Unverified Commit 866fa64d authored by Zachary Anderson's avatar Zachary Anderson Committed by GitHub

Esarbanis flutter run help (#48314)

* Reword flutter run help screen.

* As per Hixie request, added Efthymios Sarmpanis to the AUTHORS file

* fix test
Co-authored-by: 's avatarEfthymis Sarmpanis <e.sarbanis@gmail.com>
parent 76b21d28
...@@ -49,3 +49,4 @@ Robin Jespersen <info@unitedpartners.de> ...@@ -49,3 +49,4 @@ Robin Jespersen <info@unitedpartners.de>
Jefferson Quesado <jeff.quesado@gmail.com> Jefferson Quesado <jeff.quesado@gmail.com>
Mark Diener <rpzrpzrpz@gmail.com> Mark Diener <rpzrpzrpz@gmail.com>
Alek Åström <alek.astrom@gmail.com> Alek Åström <alek.astrom@gmail.com>
Efthymios Sarpmpanis <e.sarbanis@gmail.com>
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math' as math;
import '../globals.dart' as globals;
import 'terminal.dart';
const String fire = '🔥';
const int maxLineWidth = 84;
/// Encapsulates the help text construction and printing
class CommandHelp {
const CommandHelp._(this.key, this.description, [this.inParenthesis = '']);
static const CommandHelp L = CommandHelp._('L', 'Dump layer tree to the console.', 'debugDumpLayerTree');
static const CommandHelp P = CommandHelp._('P', 'Toggle performance overlay.', 'WidgetsApp.showPerformanceOverlay');
static const CommandHelp R = CommandHelp._('R', 'Hot restart.');
static const CommandHelp S = CommandHelp._('S', 'Dump accessibility tree in traversal order.', 'debugDumpSemantics');
static const CommandHelp U = CommandHelp._('U', 'Dump accessibility tree in inverse hit test order.', 'debugDumpSemantics');
static const CommandHelp a = CommandHelp._('a', 'Toggle timeline events for all widget build methods.', 'debugProfileWidgetBuilds');
static const CommandHelp d = CommandHelp._('d', 'Detach (terminate "flutter run" but leave application running).');
static const CommandHelp h = CommandHelp._('h', 'Repeat this help message.');
static const CommandHelp i = CommandHelp._('i', 'Toggle widget inspector.', 'WidgetsApp.showWidgetInspectorOverride');
static const CommandHelp o = CommandHelp._('o', 'Simulate different operating systems.', 'defaultTargetPlatform');
static const CommandHelp p = CommandHelp._('p', 'Toggle the display of construction lines.', 'debugPaintSizeEnabled');
static const CommandHelp q = CommandHelp._('q', 'Quit (terminate the application on the device).');
static const CommandHelp r = CommandHelp._('r', 'Hot reload. $fire$fire$fire');
static const CommandHelp s = CommandHelp._('s', 'Save a screenshot to flutter.png.');
static const CommandHelp t = CommandHelp._('t', 'Dump rendering tree to the console.', 'debugDumpRenderTree');
static const CommandHelp w = CommandHelp._('w', 'Dump widget hierarchy to the console.', 'debugDumpApp');
static const CommandHelp z = CommandHelp._('z', 'Toggle elevation checker.');
/// The key associated with this command
final String key;
/// A description of what this command does
final String description;
/// Text shown in parenthesis to give the context
final String inParenthesis;
bool get _hasTextInParenthesis => inParenthesis != null && inParenthesis.isNotEmpty;
int get _rawMessageLength => key.length + description.length;
@override
String toString() {
final StringBuffer message = StringBuffer();
message.writeAll(<String>[globals.terminal.bolden(key), description], ' ');
if (_hasTextInParenthesis) {
bool wrap = false;
final int maxWidth = math.max(outputPreferences.wrapColumn ?? 0, maxLineWidth);
int width = maxWidth - (globals.platform.stdoutSupportsAnsi ? _rawMessageLength + 1 : message.length);
final String parentheticalText = '($inParenthesis)';
if (width < parentheticalText.length) {
width = maxWidth;
wrap = true;
}
if (wrap) {
message.write('\n');
}
// pad according to the raw text
message.write(''.padLeft(width - parentheticalText.length));
message.write(globals.terminal.color(parentheticalText, TerminalColor.grey));
}
return message.toString();
}
void print() {
globals.printStatus(toString());
}
}
...@@ -9,6 +9,7 @@ import 'package:meta/meta.dart'; ...@@ -9,6 +9,7 @@ import 'package:meta/meta.dart';
import 'application_package.dart'; import 'application_package.dart';
import 'artifacts.dart'; import 'artifacts.dart';
import 'asset.dart'; import 'asset.dart';
import 'base/command_help.dart';
import 'base/common.dart'; import 'base/common.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
import 'base/io.dart' as io; import 'base/io.dart' as io;
...@@ -1003,23 +1004,30 @@ abstract class ResidentRunner { ...@@ -1003,23 +1004,30 @@ abstract class ResidentRunner {
void printHelp({ @required bool details }); void printHelp({ @required bool details });
void printHelpDetails() { void printHelpDetails() {
if (flutterDevices.any((FlutterDevice d) => d.device.supportsScreenshot)) {
CommandHelp.s.print();
}
if (supportsServiceProtocol) { if (supportsServiceProtocol) {
globals.printStatus('You can dump the widget hierarchy of the app (debugDumpApp) by pressing "w".'); CommandHelp.w.print();
globals.printStatus('To dump the rendering tree of the app (debugDumpRenderTree), press "t".'); CommandHelp.t.print();
if (isRunningDebug) { if (isRunningDebug) {
globals.printStatus('For layers (debugDumpLayerTree), use "L"; for accessibility (debugDumpSemantics), use "S" (for traversal order) or "U" (for inverse hit test order).'); CommandHelp.L.print();
globals.printStatus('To toggle the widget inspector (WidgetsApp.showWidgetInspectorOverride), press "i".'); CommandHelp.S.print();
globals.printStatus('To toggle the display of construction lines (debugPaintSizeEnabled), press "p".'); CommandHelp.U.print();
globals.printStatus('To simulate different operating systems, (defaultTargetPlatform), press "o".'); CommandHelp.i.print();
globals.printStatus('To toggle the elevation checker, press "z".'); CommandHelp.p.print();
CommandHelp.o.print();
CommandHelp.z.print();
} else { } else {
globals.printStatus('To dump the accessibility tree (debugDumpSemantics), press "S" (for traversal order) or "U" (for inverse hit test order).'); CommandHelp.S.print();
CommandHelp.U.print();
} }
globals.printStatus('To display the performance overlay (WidgetsApp.showPerformanceOverlay), press "P".'); // `P` should precede `a`
globals.printStatus('To enable timeline events for all widget build methods, (debugProfileWidgetBuilds), press "a"'); CommandHelp.P.print();
CommandHelp.a.print();
} }
if (flutterDevices.any((FlutterDevice d) => d.device.supportsScreenshot)) { if (flutterDevices.any((FlutterDevice d) => d.device.supportsScreenshot)) {
globals.printStatus('To save a screenshot to flutter.png, press "s".'); CommandHelp.s.print();
} }
} }
......
...@@ -6,6 +6,7 @@ import 'dart:async'; ...@@ -6,6 +6,7 @@ import 'dart:async';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'base/command_help.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
import 'device.dart'; import 'device.dart';
import 'globals.dart' as globals; import 'globals.dart' as globals;
...@@ -179,31 +180,23 @@ class ColdRunner extends ResidentRunner { ...@@ -179,31 +180,23 @@ class ColdRunner extends ResidentRunner {
@override @override
void printHelp({ @required bool details }) { void printHelp({ @required bool details }) {
bool haveDetails = false; globals.printStatus('Flutter run key commands.');
bool haveAnything = false;
for (final FlutterDevice device in flutterDevices) {
final String dname = device.device.name;
if (device.vmService != null) {
globals.printStatus('An Observatory debugger and profiler on $dname is '
'available at: ${device.vmService .httpAddress}');
}
}
if (supportsServiceProtocol) { if (supportsServiceProtocol) {
haveDetails = true;
if (details) { if (details) {
printHelpDetails(); printHelpDetails();
haveAnything = true;
} }
} }
final String quitMessage = _didAttach CommandHelp.h.print();
? 'To detach, press "d"; to quit, press "q".' if (_didAttach) {
: 'To quit, press "q".'; CommandHelp.d.print();
if (haveDetails && !details) { }
globals.printStatus('For a more detailed help message, press "h". $quitMessage'); CommandHelp.q.print();
} else if (haveAnything) { for (final FlutterDevice device in flutterDevices) {
globals.printStatus('To repeat this help message, press "h". $quitMessage'); final String dname = device.device.name;
} else { if (device.vmService != null) {
globals.printStatus(quitMessage); globals.printStatus('An Observatory debugger and profiler on $dname is '
'available at: ${device.vmService.httpAddress}');
}
} }
} }
......
...@@ -9,12 +9,12 @@ import 'package:json_rpc_2/error_code.dart' as rpc_error_code; ...@@ -9,12 +9,12 @@ import 'package:json_rpc_2/error_code.dart' as rpc_error_code;
import 'package:json_rpc_2/json_rpc_2.dart' as rpc; import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:pool/pool.dart'; import 'package:pool/pool.dart';
import 'base/async_guard.dart'; import 'base/async_guard.dart';
import 'base/command_help.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/logger.dart';
import 'base/terminal.dart';
import 'base/utils.dart'; import 'base/utils.dart';
import 'build_info.dart'; import 'build_info.dart';
import 'compile.dart'; import 'compile.dart';
...@@ -1045,29 +1045,23 @@ class HotRunner extends ResidentRunner { ...@@ -1045,29 +1045,23 @@ class HotRunner extends ResidentRunner {
@override @override
void printHelp({ @required bool details }) { void printHelp({ @required bool details }) {
const String fire = '🔥'; globals.printStatus('Flutter run key commands.');
String rawMessage = ' To hot reload changes while running, press "r". '; CommandHelp.r.print();
if (canHotRestart) { if (canHotRestart) {
rawMessage += 'To hot restart (and rebuild state), press "R".'; CommandHelp.R.print();
} }
final String message = globals.terminal.color( CommandHelp.h.print();
fire + globals.terminal.bolden(rawMessage), if (_didAttach) {
TerminalColor.red, CommandHelp.d.print();
);
globals.printStatus(message);
for (final FlutterDevice device in flutterDevices) {
final String dname = device.device.name;
globals.printStatus('An Observatory debugger and profiler on $dname is '
'available at: ${device.vmService.httpAddress}');
} }
final String quitMessage = _didAttach CommandHelp.q.print();
? 'To detach, press "d"; to quit, press "q".'
: 'To quit, press "q".';
if (details) { if (details) {
printHelpDetails(); printHelpDetails();
globals.printStatus('To repeat this help message, press "h". $quitMessage'); }
} else { for (final FlutterDevice device in flutterDevices) {
globals.printStatus('For a more detailed help message, press "h". $quitMessage'); final String dname = device.device.name;
globals.printStatus('An Observatory debugger and profiler on $dname is '
'available at:\n${device.vmService.httpAddress}');
} }
} }
......
...@@ -165,7 +165,10 @@ void main() { ...@@ -165,7 +165,10 @@ void main() {
if (message == '[stdout] Lost connection to device.') { if (message == '[stdout] Lost connection to device.') {
observatoryLogs.add(message); observatoryLogs.add(message);
} }
if (message.contains('To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".')) { if (message.contains('Hot reload.')) {
observatoryLogs.add(message);
}
if (message.contains('Hot restart.')) {
observatoryLogs.add(message); observatoryLogs.add(message);
} }
}); });
...@@ -203,14 +206,16 @@ void main() { ...@@ -203,14 +206,16 @@ void main() {
})); }));
}); });
expect(observatoryLogs.length, 7); expect(observatoryLogs.length, 9);
expect(observatoryLogs[0], '[stdout] Waiting for a connection from Flutter on MockAndroidDevice...'); expect(observatoryLogs[0], '[stdout] Waiting for a connection from Flutter on MockAndroidDevice...');
expect(observatoryLogs[1], '[verbose] Observatory URL on device: http://127.0.0.1:1234'); expect(observatoryLogs[1], '[verbose] Observatory URL on device: http://127.0.0.1:1234');
expect(observatoryLogs[2], '[stdout] Lost connection to device.'); expect(observatoryLogs[2], '[stdout] Lost connection to device.');
expect(observatoryLogs[3].contains('To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R"'), isTrue); expect(observatoryLogs[3].contains('Hot reload.'), isTrue);
expect(observatoryLogs[4], '[verbose] Observatory URL on device: http://127.0.0.1:1235'); expect(observatoryLogs[4].contains('Hot restart.'), isTrue);
expect(observatoryLogs[5], '[stdout] Lost connection to device.'); expect(observatoryLogs[5], '[verbose] Observatory URL on device: http://127.0.0.1:1235');
expect(observatoryLogs[6].contains('To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R"'), isTrue); expect(observatoryLogs[6], '[stdout] Lost connection to device.');
expect(observatoryLogs[7].contains('Hot reload.'), isTrue);
expect(observatoryLogs[8].contains('Hot restart.'), isTrue);
verify(portForwarder.forward(1234, hostPort: anyNamed('hostPort'))).called(1); verify(portForwarder.forward(1234, hostPort: anyNamed('hostPort'))).called(1);
verify(portForwarder.forward(1235, hostPort: anyNamed('hostPort'))).called(1); verify(portForwarder.forward(1235, hostPort: anyNamed('hostPort'))).called(1);
......
...@@ -6,6 +6,7 @@ import 'dart:async'; ...@@ -6,6 +6,7 @@ import 'dart:async';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/command_help.dart';
import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/context.dart'; import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
...@@ -365,21 +366,33 @@ void main() { ...@@ -365,21 +366,33 @@ void main() {
// supports service protocol // supports service protocol
expect(residentRunner.supportsServiceProtocol, true); expect(residentRunner.supportsServiceProtocol, true);
expect(testLogger.statusText, contains('"w"'));
expect(testLogger.statusText, contains('"t"'));
expect(testLogger.statusText, contains('"P"'));
expect(testLogger.statusText, contains('"a"'));
// isRunningDebug // isRunningDebug
expect(residentRunner.isRunningDebug, true); expect(residentRunner.isRunningDebug, true);
expect(testLogger.statusText, contains('"L"')); // commands
expect(testLogger.statusText, contains('"S"')); expect(testLogger.statusText, equals(
expect(testLogger.statusText, contains('"U"')); <dynamic>[
expect(testLogger.statusText, contains('"i"')); 'Flutter run key commands.',
expect(testLogger.statusText, contains('"p"')); CommandHelp.r,
expect(testLogger.statusText, contains('"o"')); CommandHelp.R,
expect(testLogger.statusText, contains('"z"')); CommandHelp.h,
// screenshot CommandHelp.q,
expect(testLogger.statusText, contains('"s"')); CommandHelp.s,
CommandHelp.w,
CommandHelp.t,
CommandHelp.L,
CommandHelp.S,
CommandHelp.U,
CommandHelp.i,
CommandHelp.p,
CommandHelp.o,
CommandHelp.z,
CommandHelp.P,
CommandHelp.a,
CommandHelp.s,
'An Observatory debugger and profiler on null is available at:\nnull',
''
].join('\n')
));
})); }));
test('ResidentRunner can take screenshot on debug device', () => testbed.run(() async { test('ResidentRunner can take screenshot on debug device', () => testbed.run(() async {
......
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