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

Debugger: soft wrap console input lines #8855

Merged
merged 5 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
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
87 changes: 48 additions & 39 deletions packages/devtools_app/lib/src/shared/console/console.dart
Original file line number Diff line number Diff line change
Expand Up @@ -162,46 +162,55 @@ class _ConsoleOutputState extends State<_ConsoleOutput>
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: denseSpacing),
child: SelectionArea(
child: ListView.separated(
padding: const EdgeInsets.all(denseSpacing),
itemCount: _currentLines.length + (widget.footer != null ? 1 : 0),
controller: _scroll,
// Scroll physics to try to keep content within view and avoid bouncing.
physics: const ClampingScrollPhysics(
parent: RangeMaintainingScrollPhysics(),
),
separatorBuilder: (_, _) {
return const PaddedDivider.noPadding();
},
itemBuilder: (context, index) {
if (index == _currentLines.length && widget.footer != null) {
return widget.footer!;
}
final line = _currentLines[index];
if (line is TextConsoleLine) {
return Text.rich(
TextSpan(
// TODO(jacobr): consider caching the processed ansi terminal
// codes.
children: textSpansFromAnsi(
line.text,
theme.regularTextStyle,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: ListView.separated(
padding: const EdgeInsets.all(denseSpacing),
itemCount: _currentLines.length,
controller: _scroll,
// Scroll physics to try to keep content within view and avoid bouncing.
physics: const ClampingScrollPhysics(
parent: RangeMaintainingScrollPhysics(),
),
);
} else if (line is VariableConsoleLine) {
return ExpandableVariable(
variable: line.variable,
isSelectable: false,
);
} else {
assert(
false,
'ConsoleLine of unsupported type ${line.runtimeType} encountered',
);
return const SizedBox();
}
},
separatorBuilder: (_, _) {
return const PaddedDivider.noPadding();
},
itemBuilder: (context, index) {
final line = _currentLines[index];
if (line is TextConsoleLine) {
return Text.rich(
TextSpan(
// TODO(jacobr): consider caching the processed ansi terminal
// codes.
children: textSpansFromAnsi(
line.text,
theme.regularTextStyle,
),
),
);
} else if (line is VariableConsoleLine) {
return ExpandableVariable(
variable: line.variable,
isSelectable: false,
);
} else {
assert(
false,
'ConsoleLine of unsupported type ${line.runtimeType} encountered',
);
return const SizedBox();
}
},
),
),
// consider constraining a max height.
Padding(
padding: const EdgeInsets.only(top: denseSpacing),
child: widget.footer!,
),
],
),
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ class ExpressionEvalField extends StatefulWidget {

final AutoCompleteResultsFunction getAutoCompleteResults;

static const _evalFieldHeight = 32.0;

@override
ExpressionEvalFieldState createState() => ExpressionEvalFieldState();
}
Expand Down Expand Up @@ -218,46 +216,44 @@ class ExpressionEvalFieldState extends State<ExpressionEvalField>

return KeyEventResult.ignored;
},
child: SizedBox(
height: ExpressionEvalField._evalFieldHeight,
child: AutoCompleteSearchField(
controller: _autoCompleteController,
searchFieldEnabled: true,
shouldRequestFocus: false,
clearFieldOnEscapeWhenOverlayHidden: true,
onSelection: _onSelection,
decoration: InputDecoration(
contentPadding: const EdgeInsets.all(denseSpacing),
border: const OutlineInputBorder(),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide.none,
),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide.none,
),
labelText: 'Eval. Enter "?" for help.',
labelStyle: Theme.of(context).subtleTextStyle,
child: AutoCompleteSearchField(
controller: _autoCompleteController,
searchFieldEnabled: true,
shouldRequestFocus: false,
clearFieldOnEscapeWhenOverlayHidden: true,
onSelection: _onSelection,
decoration: InputDecoration(
contentPadding: const EdgeInsets.all(denseSpacing),
border: const OutlineInputBorder(),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide.none,
),
overlayXPositionBuilder: (
String inputValue,
TextStyle? inputStyle,
) {
// X-coordinate is equivalent to the width of the input text
// up to the last "." or the insertion point (cursor):
final indexOfDot = inputValue.lastIndexOf('.');
final textSegment =
indexOfDot != -1
? inputValue.substring(0, indexOfDot + 1)
: inputValue;
return calculateTextSpanWidth(
TextSpan(text: textSegment, style: inputStyle),
);
},
// Disable ligatures, so the suggestions of the auto complete work correcly.
style: Theme.of(context).fixedFontStyle.copyWith(
fontFeatures: [const FontFeature.disable('liga')],
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide.none,
),
labelText: 'Eval. Enter "?" for help.',
labelStyle: Theme.of(context).subtleTextStyle,
),
overlayXPositionBuilder: (
String inputValue,
TextStyle? inputStyle,
) {
// X-coordinate is equivalent to the width of the input text
// up to the last "." or the insertion point (cursor):
final indexOfDot = inputValue.lastIndexOf('.');
final textSegment =
indexOfDot != -1
? inputValue.substring(0, indexOfDot + 1)
: inputValue;
return calculateTextSpanWidth(
TextSpan(text: textSegment, style: inputStyle),
);
},
// Disable ligatures, so the suggestions of the auto complete work correcly.
style: Theme.of(context).fixedFontStyle.copyWith(
fontFeatures: [const FontFeature.disable('liga')],
),
maxLines: null,
),
),
),
Expand Down
57 changes: 43 additions & 14 deletions packages/devtools_app/lib/src/shared/ui/search.dart
Original file line number Diff line number Diff line change
Expand Up @@ -893,8 +893,11 @@ class SearchField<T extends SearchControllerMixin> extends StatefulWidget {
this.onClose,
this.searchFieldWidth = defaultSearchFieldWidth,
double? searchFieldHeight,
int? maxLines = 1,
super.key,
}) : searchFieldHeight = searchFieldHeight ?? defaultTextFieldHeight;
}) : assert(maxLines != 0, "'maxLines' must not be 0"),
searchFieldHeight = searchFieldHeight ?? defaultTextFieldHeight,
_maxLines = maxLines;

final T searchController;

Expand All @@ -917,6 +920,11 @@ class SearchField<T extends SearchControllerMixin> extends StatefulWidget {
/// triggered.
final VoidCallback? onClose;

/// The maximum number of lines, by default one.
///
/// Can be set to null to remove the restriction; must not be zero.
final int? _maxLines;

@override
State<SearchField> createState() => _SearchFieldState();
}
Expand All @@ -928,18 +936,23 @@ class _SearchFieldState extends State<SearchField>

@override
Widget build(BuildContext context) {
return SizedBox(
width: widget.searchFieldWidth,
height: widget.searchFieldHeight,
child: StatelessSearchField(
controller: searchController,
searchFieldEnabled: widget.searchFieldEnabled,
shouldRequestFocus: widget.shouldRequestFocus,
supportsNavigation: widget.supportsNavigation,
onClose: widget.onClose,
searchFieldHeight: widget.searchFieldHeight,
),
final searchField = StatelessSearchField(
controller: searchController,
searchFieldEnabled: widget.searchFieldEnabled,
shouldRequestFocus: widget.shouldRequestFocus,
supportsNavigation: widget.supportsNavigation,
onClose: widget.onClose,
searchFieldHeight: widget.searchFieldHeight,
maxLines: widget._maxLines,
);

return widget._maxLines != 1
? searchField
: SizedBox(
width: widget.searchFieldWidth,
height: widget.searchFieldHeight,
child: searchField,
);
}
}

Expand Down Expand Up @@ -970,7 +983,9 @@ class StatelessSearchField<T extends SearchableDataMixin>
this.suffix,
this.style,
this.searchFieldHeight,
});
int? maxLines = 1,
}) : assert(maxLines != 0, "'maxLines' must not be 0"),
_maxLines = maxLines;

final SearchControllerMixin<T> controller;

Expand Down Expand Up @@ -1014,6 +1029,11 @@ class StatelessSearchField<T extends SearchableDataMixin>

final double? searchFieldHeight;

/// The maximum number of lines, by default one.
///
/// Can be set to null to remove the restriction; must not be zero.
final int? _maxLines;

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
Expand All @@ -1032,6 +1052,7 @@ class StatelessSearchField<T extends SearchableDataMixin>
focusNode: controller.searchFieldFocusNode,
controller: controller.searchTextFieldController,
style: textStyle,
maxLines: _maxLines,
onChanged: onChanged,
onEditingComplete: () {
controller.searchFieldFocusNode?.requestFocus();
Expand Down Expand Up @@ -1116,7 +1137,9 @@ class AutoCompleteSearchField extends StatefulWidget {
this.onFocusLost,
this.style,
this.keyEventsToIgnore = const {},
});
int? maxLines = 1,
}) : assert(maxLines != 0, "'maxLines' must not be 0"),
_maxLines = maxLines;

final AutoCompleteSearchControllerMixin controller;

Expand Down Expand Up @@ -1164,6 +1187,11 @@ class AutoCompleteSearchField extends StatefulWidget {
/// [controller.autocompleteFocusNode] has lost focus.
final VoidCallback? onFocusLost;

/// The maximum number of lines, by default one.
///
/// Can be set to null to remove the restriction; must not be zero.
final int? _maxLines;

@override
State<AutoCompleteSearchField> createState() =>
_AutoCompleteSearchFieldState();
Expand Down Expand Up @@ -1214,6 +1242,7 @@ class _AutoCompleteSearchFieldState extends State<AutoCompleteSearchField>
},
onClose: widget.onClose,
style: widget.style,
maxLines: widget._maxLines,
),
),
);
Expand Down
2 changes: 2 additions & 0 deletions packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ TODO: Remove this section if there are not any general updates.
* Updated syntax highlighting with support for digit separators,
and improved comment and string interpolation handling. -
[#8861](https://github.com/flutter/devtools/pull/8861)
* Added soft line wrapping in the debugger console.
[#8855](https://github.com/flutter/devtools/pull/8855).

## Network profiler updates

Expand Down
Loading