Skip to content
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
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,4 @@ Pedro Massango <pedromassango.developer@gmail.com>
Hidenori Matsubayashi <Hidenori.Matsubayashi@sony.com>
Perqin Xie <perqinxie@gmail.com>
Seongyun Kim <helloworld@cau.ac.kr>
Ludwik Trammer <ludwik@gmail.com>
2 changes: 1 addition & 1 deletion dev/bots/analyze.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1197,7 +1197,7 @@ Future<void> _runFlutterAnalyze(String workingDirectory, {
}) async {
return runCommand(
flutter,
<String>['analyze', '--dartdocs', ...options],
<String>['analyze', ...options],
workingDirectory: workingDirectory,
);
}
Expand Down
63 changes: 3 additions & 60 deletions dev/devicelab/bin/tasks/dartdocs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,70 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:convert';
import 'dart:io';

import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;

Future<void> main() async {
final String dot = Platform.isWindows ? '-' : '•';
await flutter('update-packages');
await task(() async {
final Stopwatch clock = Stopwatch()..start();
final Process analysis = await startProcess(
path.join(flutterDirectory.path, 'bin', 'flutter'),
<String>['analyze', '--no-preamble', '--flutter-repo', '--dartdocs'],
workingDirectory: flutterDirectory.path,
);
int publicMembers = 0;
int otherErrors = 0;
int otherLines = 0;
bool sawFinalLine = false;
await for (String entry in analysis.stdout.transform<String>(utf8.decoder).transform<String>(const LineSplitter())) {
entry = entry.trim();
print('analyzer stdout: $entry');
if (entry == 'Building flutter tool...') {
// ignore this line
} else if (entry.startsWith('info $dot Document all public members $dot')) {
publicMembers += 1;
} else if (entry.startsWith('info $dot') || entry.startsWith('warning $dot') || entry.startsWith('error $dot')) {
otherErrors += 1;
} else if (entry.contains(' (ran in ') && !sawFinalLine) {
// ignore this line once
sawFinalLine = true;
} else if (entry.isNotEmpty) {
otherLines += 1;
print('^ not sure what to do with that line ^');
}
}
await for (final String entry in analysis.stderr.transform<String>(utf8.decoder).transform<String>(const LineSplitter())) {
print('analyzer stderr: $entry');
if (entry.contains(' (ran in ') && !sawFinalLine) {
// ignore this line once
sawFinalLine = true;
} else {
otherLines += 1;
print('^ not sure what to do with that line ^');
}
}
final int result = await analysis.exitCode;
clock.stop();
if (!sawFinalLine)
throw Exception('flutter analyze did not output final message');
if (publicMembers == 0 && otherErrors == 0 && result != 0)
throw Exception('flutter analyze exited with unexpected error code $result');
if (publicMembers != 0 && otherErrors != 0 && result == 0)
throw Exception('flutter analyze exited with successful status code despite reporting errors');
if (otherLines != 0)
throw Exception('flutter analyze had unexpected output (we saw $otherLines unexpected line${ otherLines == 1 ? "" : "s" })');
final Map<String, dynamic> data = <String, dynamic>{
'members_missing_dartdocs': publicMembers,
'analysis_errors': otherErrors,
'elapsed_time_ms': clock.elapsedMilliseconds,
};
return TaskResult.success(data, benchmarkScoreKeys: data.keys.toList());
// TODO(jmagman): Remove once dartdocs builder can be deleted
// when https://github.com/flutter/flutter/issues/79798 rolls to stable.
return TaskResult.success(null);
});
}
6 changes: 0 additions & 6 deletions dev/prod_builders.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,6 @@
"task_name": "linux_customer_testing",
"flaky": false
},
{
"name": "Linux dartdocs",
"repo": "flutter",
"task_name": "linux_dartdocs",
"flaky": false
},
{
"name": "Linux docs_test",
"repo": "flutter",
Expand Down
9 changes: 5 additions & 4 deletions packages/flutter/lib/src/rendering/editable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3703,7 +3703,10 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
_clipRectLayer = null;
_paintContents(context, offset);
}
_paintHandleLayers(context, getEndpointsForSelection(selection!));
final TextSelection? selection = this.selection;
if (selection != null) {
_paintHandleLayers(context, getEndpointsForSelection(selection));
}
}

ClipRectLayer? _clipRectLayer;
Expand Down Expand Up @@ -4060,9 +4063,7 @@ class _FloatingCursorPainter extends RenderEditablePainter {
assert(renderEditable != null);
final TextSelection? selection = renderEditable.selection;

// TODO(LongCatIsLooong): skip painting the caret when the selection is
// (-1, -1).
if (selection == null || !selection.isCollapsed)
if (selection == null || !selection.isCollapsed || !selection.isValid)
return;

final Rect? floatingCursorRect = this.floatingCursorRect;
Expand Down
4 changes: 2 additions & 2 deletions packages/flutter/lib/src/widgets/text_selection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -963,8 +963,8 @@ class TextSelectionGestureDetectorBuilder {
renderEditable.lastSecondaryTapDownPosition!,
);

return renderEditable.selection!.base.offset <= textPosition.offset
&& renderEditable.selection!.extent.offset >= textPosition.offset;
return renderEditable.selection!.start <= textPosition.offset
&& renderEditable.selection!.end >= textPosition.offset;
}

/// Whether to show the selection toolbar.
Expand Down
45 changes: 45 additions & 0 deletions packages/flutter/test/rendering/editable_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,51 @@ void main() {
expect(editable, paintsExactlyCountTimes(#drawRect, 1));
});

test('does not paint the caret when selection is null', () async {
final TextSelectionDelegate delegate = FakeEditableTextState();
final ValueNotifier<bool> showCursor = ValueNotifier<bool>(true);
final RenderEditable editable = RenderEditable(
backgroundCursorColor: Colors.grey,
selectionColor: Colors.black,
paintCursorAboveText: true,
textDirection: TextDirection.ltr,
cursorColor: Colors.red,
showCursor: showCursor,
offset: ViewportOffset.zero(),
textSelectionDelegate: delegate,
text: const TextSpan(
text: 'test',
style: TextStyle(
height: 1.0, fontSize: 10.0, fontFamily: 'Ahem',
),
),
startHandleLayerLink: LayerLink(),
endHandleLayerLink: LayerLink(),
selection: const TextSelection.collapsed(
offset: 2,
affinity: TextAffinity.upstream,
),
);

layout(editable);

expect(
editable,
paints
..paragraph()
// Red collapsed cursor is painted, not a selection box.
..rect(color: Colors.red[500]),
);

// Let the RenderEditable paint again. Setting the selection to null should
// prevent the caret from being painted.
editable.selection = null;
// Still paints the paragraph.
expect(editable, paints..paragraph());
// No longer paints the caret.
expect(editable, isNot(paints..rect(color: Colors.red[500])));
});

test('selects correct place with offsets', () {
const String text = 'test\ntest';
final TextSelectionDelegate delegate = FakeEditableTextState()
Expand Down
45 changes: 44 additions & 1 deletion packages/flutter/test/widgets/text_selection_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// found in the LICENSE file.

import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/gestures.dart' show PointerDeviceKind;
import 'package:flutter/gestures.dart' show PointerDeviceKind, kSecondaryButton;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
Expand Down Expand Up @@ -487,6 +487,49 @@ void main() {
expect(renderEditable.selectPositionAtCalled, isTrue);
});

testWidgets('TextSelectionGestureDetectorBuilder right click', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/80119
await pumpTextSelectionGestureDetectorBuilder(tester);

final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
renderEditable.text = const TextSpan(text: 'one two three four five six seven');
await tester.pump();

final TestGesture gesture = await tester.createGesture(
pointer: 0,
kind: PointerDeviceKind.mouse,
buttons: kSecondaryButton,
);
addTearDown(gesture.removePointer);

// Get the location of the 10th character
final Offset charLocation = renderEditable
.getLocalRectForCaret(const TextPosition(offset: 10)).center;
final Offset globalCharLocation = charLocation + tester.getTopLeft(find.byType(FakeEditable));

// Right clicking on a word should select it
await gesture.down(globalCharLocation);
await gesture.up();
await tester.pump();
expect(renderEditable.selectWordCalled, isTrue);

// Right clicking on a word within a selection shouldn't change the selection
renderEditable.selectWordCalled = false;
renderEditable.selection = const TextSelection(baseOffset: 3, extentOffset: 20);
await gesture.down(globalCharLocation);
await gesture.up();
await tester.pump();
expect(renderEditable.selectWordCalled, isFalse);

// Right clicking on a word within a reverse (right-to-left) selection shouldn't change the selection
renderEditable.selectWordCalled = false;
renderEditable.selection = const TextSelection(baseOffset: 20, extentOffset: 3);
await gesture.down(globalCharLocation);
await gesture.up();
await tester.pump();
expect(renderEditable.selectWordCalled, isFalse);
});

testWidgets('test TextSelectionGestureDetectorBuilder tap', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester);
final TestGesture gesture = await tester.startGesture(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// ignore_for_file: public_member_api_docs

// This is the test for the private implementation of animated icons.
// To make the private API accessible from the test we do not import the
// material material_animated_icons library, but instead, this test file is an
Expand Down
4 changes: 2 additions & 2 deletions packages/flutter_tools/lib/src/commands/analyze.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ class AnalyzeCommand extends FlutterCommand {
help: 'Analyze the current project, if applicable.', defaultsTo: true);
argParser.addFlag('dartdocs',
negatable: false,
help: 'List every public member that is lacking documentation. '
'(The "public_member_api_docs" lint must be enabled in "analysis_options.yaml".)',
help: '(deprecated) List every public member that is lacking documentation. '
'This command will be removed in a future version of Flutter.',
hide: !verboseHelp);
argParser.addFlag('watch',
help: 'Run analysis continuously, watching the filesystem for changes.',
Expand Down
38 changes: 2 additions & 36 deletions packages/flutter_tools/lib/src/commands/analyze_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import '../base/platform.dart';
import '../base/terminal.dart';
import '../base/utils.dart';
import '../cache.dart';
import '../dart/analysis.dart';
import '../globals_null_migrated.dart' as globals;

/// Common behavior for `flutter analyze` and `flutter analyze --watch`
Expand Down Expand Up @@ -71,12 +70,11 @@ abstract class AnalyzeBase {
}
}

void writeBenchmark(Stopwatch stopwatch, int errorCount, int membersMissingDocumentation) {
void writeBenchmark(Stopwatch stopwatch, int errorCount) {
const String benchmarkOut = 'analysis_benchmark.json';
final Map<String, dynamic> data = <String, dynamic>{
'time': stopwatch.elapsedMilliseconds / 1000.0,
'issues': errorCount,
'missingDartDocs': membersMissingDocumentation,
};
fileSystem.file(benchmarkOut).writeAsStringSync(toPrettyJson(data));
logger.printStatus('Analysis benchmark written to $benchmarkOut ($data).');
Expand All @@ -85,41 +83,14 @@ abstract class AnalyzeBase {
bool get isFlutterRepo => argResults['flutter-repo'] as bool;
String get sdkPath => argResults['dart-sdk'] as String ?? artifacts.getArtifactPath(Artifact.engineDartSdkPath);
bool get isBenchmarking => argResults['benchmark'] as bool;
bool get isDartDocs => argResults['dartdocs'] as bool;
String get protocolTrafficLog => argResults['protocol-traffic-log'] as String;

static int countMissingDartDocs(List<AnalysisError> errors) {
return errors.where((AnalysisError error) {
return error.code == 'public_member_api_docs';
}).length;
}

static String generateDartDocMessage(int undocumentedMembers) {
String dartDocMessage;

assert(undocumentedMembers >= 0);
switch (undocumentedMembers) {
case 0:
dartDocMessage = 'all public member have documentation';
break;
case 1:
dartDocMessage = 'one public member lacks documentation';
break;
default:
dartDocMessage = '$undocumentedMembers public members lack documentation';
}

return dartDocMessage;
}

/// Generate an analysis summary for both [AnalyzeOnce], [AnalyzeContinuously].
static String generateErrorsMessage({
@required int issueCount,
int issueDiff,
int files,
@required String seconds,
int undocumentedMembers = 0,
String dartDocMessage = '',
}) {
final StringBuffer errorsMessage = StringBuffer(issueCount > 0
? '$issueCount ${pluralize('issue', issueCount)} found.'
Expand All @@ -138,12 +109,7 @@ abstract class AnalyzeBase {
if (files != null) {
errorsMessage.write(' • analyzed $files ${pluralize('file', files)}');
}

if (undocumentedMembers > 0) {
errorsMessage.write(' (ran in ${seconds}s; $dartDocMessage)');
} else {
errorsMessage.write(' (ran in ${seconds}s)');
}
errorsMessage.write(' (ran in ${seconds}s)');
return errorsMessage.toString();
}
}
Expand Down
15 changes: 2 additions & 13 deletions packages/flutter_tools/lib/src/commands/analyze_continuously.dart
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,6 @@ class AnalyzeContinuously extends AnalyzeBase {
}
}

int issueCount = errors.length;

// count missing dartdocs
final int undocumentedMembers = AnalyzeBase.countMissingDartDocs(errors);
if (!isDartDocs) {
errors.removeWhere((AnalysisError error) => error.code == 'public_member_api_docs');
issueCount -= undocumentedMembers;
}

errors.sort();

for (final AnalysisError error in errors) {
Expand All @@ -142,23 +133,21 @@ class AnalyzeContinuously extends AnalyzeBase {

dumpErrors(errors.map<String>((AnalysisError error) => error.toLegacyString()));

final int issueCount = errors.length;
final int issueDiff = issueCount - lastErrorCount;
lastErrorCount = issueCount;
final String seconds = (analysisTimer.elapsedMilliseconds / 1000.0).toStringAsFixed(2);
final String dartDocMessage = AnalyzeBase.generateDartDocMessage(undocumentedMembers);
final String errorsMessage = AnalyzeBase.generateErrorsMessage(
issueCount: issueCount,
issueDiff: issueDiff,
files: analyzedPaths.length,
seconds: seconds,
undocumentedMembers: undocumentedMembers,
dartDocMessage: dartDocMessage,
);

logger.printStatus(errorsMessage);

if (firstAnalysis && isBenchmarking) {
writeBenchmark(analysisTimer, issueCount, undocumentedMembers);
writeBenchmark(analysisTimer, issueCount);
server.dispose().whenComplete(() { exit(issueCount > 0 ? 1 : 0); });
}

Expand Down
Loading