From 10e4b040108af9b8c4e53abfaefcb3ca45812e0a Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Wed, 11 Aug 2021 19:48:29 -0700 Subject: [PATCH] Switch document generation to use the snippets package (#87231) Switch document generation to use the snippets package instead of the snippets code in the Flutter repo. In the process, some bugs in sample code analysis have been fixed, and I've fixed some more errors in the samples. This will allow the snippets package to be developed separately from the Flutter repo, and reduce the code in the Flutter repo. The snippets code is deleted in this PR. I also converted some comments in the snippet templates to be regular comments instead of doc comments, because having a doc comment block before the imports causes the Dart import sorter to lose the comment. They should have been regular comments in the first place. The snippets package resides in the assets-for-api-docs repo. The sample analysis has also been converted to be run in parallel, and I've bumped the Dartdoc version to 1.0.2. --- .cirrus.yml | 2 +- dartdoc_options.yaml | 11 +- dev/bots/analyze_sample_code.dart | 307 ++++++++++----- dev/bots/docs.sh | 28 +- dev/bots/test.dart | 1 - .../analyze-sample-code-test-dart-ui/ui.dart | 4 +- dev/bots/test/analyze_sample_code_test.dart | 30 +- .../config/skeletons/dartpad-sample.html | 22 -- dev/snippets/config/skeletons/snippet.html | 3 - dev/snippets/config/templates/freeform.tmpl | 4 +- .../config/templates/stateful_widget.tmpl | 6 +- .../templates/stateful_widget_cupertino.tmpl | 6 +- ...ateful_widget_cupertino_page_scaffold.tmpl | 6 +- .../stateful_widget_cupertino_ticker.tmpl | 6 +- .../templates/stateful_widget_material.tmpl | 6 +- .../stateful_widget_material_ticker.tmpl | 6 +- .../stateful_widget_restoration.tmpl | 4 +- ...stateful_widget_restoration_cupertino.tmpl | 4 +- .../stateful_widget_restoration_material.tmpl | 4 +- .../templates/stateful_widget_scaffold.tmpl | 6 +- .../stateful_widget_scaffold_center.tmpl | 6 +- ...widget_scaffold_center_freeform_state.tmpl | 4 +- .../templates/stateful_widget_ticker.tmpl | 6 +- .../config/templates/stateless_widget.tmpl | 6 +- .../templates/stateless_widget_cupertino.tmpl | 6 +- ...teless_widget_cupertino_page_scaffold.tmpl | 6 +- .../templates/stateless_widget_material.tmpl | 6 +- ...tateless_widget_restoration_cupertino.tmpl | 6 +- ...stateless_widget_restoration_material.tmpl | 6 +- .../templates/stateless_widget_scaffold.tmpl | 6 +- .../stateless_widget_scaffold_center.tmpl | 6 +- dev/snippets/lib/configuration.dart | 80 ---- dev/snippets/lib/main.dart | 241 ------------ dev/snippets/lib/snippets.dart | 350 ------------------ dev/snippets/pubspec.yaml | 99 ----- dev/snippets/test/configuration_test.dart | 52 --- dev/snippets/test/snippets_test.dart | 334 ----------------- dev/tools/dartdoc.dart | 19 +- .../lib/src/cupertino/page_scaffold.dart | 2 +- .../flutter/lib/src/material/app_bar.dart | 2 +- .../flutter/lib/src/material/data_table.dart | 2 +- .../lib/src/material/input_decorator.dart | 2 +- .../lib/src/material/progress_indicator.dart | 2 +- .../flutter/lib/src/painting/gradient.dart | 4 +- .../flutter/lib/src/widgets/autocomplete.dart | 2 +- .../lib/src/widgets/focus_manager.dart | 2 +- .../flutter/lib/src/widgets/framework.dart | 2 +- 47 files changed, 347 insertions(+), 1378 deletions(-) delete mode 100644 dev/snippets/lib/configuration.dart delete mode 100644 dev/snippets/lib/main.dart delete mode 100644 dev/snippets/lib/snippets.dart delete mode 100644 dev/snippets/pubspec.yaml delete mode 100644 dev/snippets/test/configuration_test.dart delete mode 100644 dev/snippets/test/snippets_test.dart diff --git a/.cirrus.yml b/.cirrus.yml index 2433c6df473..9499f293b76 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -138,7 +138,7 @@ task: - name: docs-linux # linux-only environment: CPU: 4 - MEMORY: 8G + MEMORY: 12G only_if: "$CIRRUS_PR != ''" script: - ./dev/bots/docs.sh diff --git a/dartdoc_options.yaml b/dartdoc_options.yaml index cbef352566d..d82f23c1efb 100644 --- a/dartdoc_options.yaml +++ b/dartdoc_options.yaml @@ -1,16 +1,17 @@ # This file is used by dartdoc when generating API documentation for Flutter. dartdoc: - # Before you can run dartdoc, the snippets tool needs to have a snapshot built. - # The dev/tools/dartdoc.dart script does this automatically. + # Before you can run dartdoc, the snippets tool needs to be + # activated with "pub global activate snippets " + # The dev/bots/docs.sh script does this automatically. tools: snippet: - command: ["dev/snippets/lib/main.dart", "--type=snippet"] + command: ["bin/cache/dart-sdk/bin/pub", "global", "run", "snippets", "--output-directory=doc/snippets", "--type=snippet"] description: "Creates sample code documentation output from embedded documentation samples." sample: - command: ["dev/snippets/lib/main.dart", "--type=sample"] + command: ["bin/cache/dart-sdk/bin/pub", "global", "run", "snippets", "--output-directory=doc/snippets", "--type=sample"] description: "Creates full application sample code documentation output from embedded documentation samples." dartpad: - command: ["dev/snippets/lib/main.dart", "--type=sample", "--dartpad"] + command: ["bin/cache/dart-sdk/bin/pub", "global", "run", "snippets", "--output-directory=doc/snippets", "--type=dartpad"] description: "Creates full application sample code documentation output from embedded documentation samples and displays it in an embedded DartPad." errors: # Default errors of dartdoc: diff --git a/dev/bots/analyze_sample_code.dart b/dev/bots/analyze_sample_code.dart index e82abedfb0a..beacde99b46 100644 --- a/dev/bots/analyze_sample_code.dart +++ b/dev/bots/analyze_sample_code.dart @@ -7,8 +7,10 @@ // To run this, from the root of the Flutter repository: // bin/cache/dart-sdk/bin/dart dev/bots/analyze_sample_code.dart -// @dart= 2.12 +// @dart= 2.14 +import 'dart:async'; +import 'dart:collection'; import 'dart:convert'; import 'dart:io'; @@ -16,12 +18,22 @@ import 'package:args/args.dart'; import 'package:path/path.dart' as path; import 'package:watcher/watcher.dart'; +// If you update this version, also update it in dev/bots/docs.sh +const String _snippetsActivateVersion = '0.2.2'; + final String _flutterRoot = path.dirname(path.dirname(path.dirname(path.fromUri(Platform.script)))); final String _defaultFlutterPackage = path.join(_flutterRoot, 'packages', 'flutter', 'lib'); final String _defaultDartUiLocation = path.join(_flutterRoot, 'bin', 'cache', 'pkg', 'sky_engine', 'lib', 'ui'); final String _flutter = path.join(_flutterRoot, 'bin', Platform.isWindows ? 'flutter.bat' : 'flutter'); -void main(List arguments) { +/// Finds the location of the pub executable, with the assumption that it is +/// in the same location as the Dart executable used to run this script. +String get _pubExecutable { + final File dartExecutable = File(Platform.resolvedExecutable); + return path.join(path.dirname(dartExecutable.absolute.path), Platform.isWindows ? 'pub.exe' : 'pub'); +} + +Future main(List arguments) async { final ArgParser argParser = ArgParser(); argParser.addOption( 'temp', @@ -61,6 +73,13 @@ void main(List arguments) { abbr: 'i', help: 'Analyzes the sample code in the specified file interactively.', ); + argParser.addFlag( + 'global-activate-snippets', + defaultsTo: true, + negatable: true, + help: 'Whether or not to "pub global activate" the snippets package. If set, will ' + 'activate version $_snippetsActivateVersion', + ); final ArgResults parsedArguments = argParser.parse(arguments); @@ -107,8 +126,25 @@ void main(List arguments) { tempDirectory.createSync(); } + if (parsedArguments['global-activate-snippets']! as bool) { + try { + Process.runSync( + _pubExecutable, + [ + 'global', + 'activate', + 'snippets', + _snippetsActivateVersion, + ], + workingDirectory: _flutterRoot, + ); + } on ProcessException catch (e) { + stderr.writeln('Unable to global activate snippets package at version $_snippetsActivateVersion: $e'); + exit(1); + } + } if (parsedArguments['interactive'] != null) { - _runInteractive( + await _runInteractive( tempDir: tempDirectory, flutterPackage: flutterPackage, filePath: parsedArguments['interactive'] as String, @@ -116,7 +152,7 @@ void main(List arguments) { ); } else { try { - exitCode = SampleChecker( + exitCode = await SampleChecker( flutterPackage, tempDirectory: tempDirectory, verbose: parsedArguments['verbose'] as bool, @@ -129,6 +165,95 @@ void main(List arguments) { } } +typedef TaskQueueClosure = Future Function(); + +class _TaskQueueItem { + _TaskQueueItem(this._closure, this._completer, {this.onComplete}); + + final TaskQueueClosure _closure; + final Completer _completer; + void Function()? onComplete; + + Future run() async { + try { + _completer.complete(await _closure()); + } catch (e) { + _completer.completeError(e); + } finally { + onComplete?.call(); + } + } +} + +/// A task queue of Futures to be completed in parallel, throttling +/// the number of simultaneous tasks. +/// +/// The tasks return results of type T. +class TaskQueue { + /// Creates a task queue with a maximum number of simultaneous jobs. + /// The [maxJobs] parameter defaults to the number of CPU cores on the + /// system. + TaskQueue({int? maxJobs}) + : maxJobs = maxJobs ?? Platform.numberOfProcessors; + + /// The maximum number of jobs that this queue will run simultaneously. + final int maxJobs; + + final Queue<_TaskQueueItem> _pendingTasks = Queue<_TaskQueueItem>(); + final Set<_TaskQueueItem> _activeTasks = <_TaskQueueItem>{}; + final Set> _completeListeners = >{}; + + /// Returns a future that completes when all tasks in the [TaskQueue] are + /// complete. + Future get tasksComplete { + // In case this is called when there are no tasks, we want it to + // signal complete immediately. + if (_activeTasks.isEmpty && _pendingTasks.isEmpty) { + return Future.value(); + } + final Completer completer = Completer(); + _completeListeners.add(completer); + return completer.future; + } + + /// Adds a single closure to the task queue, returning a future that + /// completes when the task completes. + Future add(TaskQueueClosure task) { + final Completer completer = Completer(); + _pendingTasks.add(_TaskQueueItem(task, completer)); + if (_activeTasks.length < maxJobs) { + _processTask(); + } + return completer.future; + } + + // Process a single task. + void _processTask() { + if (_pendingTasks.isNotEmpty && _activeTasks.length <= maxJobs) { + final _TaskQueueItem item = _pendingTasks.removeFirst(); + _activeTasks.add(item); + item.onComplete = () { + _activeTasks.remove(item); + _processTask(); + }; + item.run(); + } else { + _checkForCompletion(); + } + } + + void _checkForCompletion() { + if (_activeTasks.isEmpty && _pendingTasks.isEmpty) { + for (final Completer completer in _completeListeners) { + if (!completer.isCompleted) { + completer.complete(); + } + } + _completeListeners.clear(); + } + } +} + class SampleCheckerException implements Exception { SampleCheckerException(this.message, {this.file, this.line}); final String message; @@ -147,6 +272,12 @@ class SampleCheckerException implements Exception { } } +class AnalysisResult { + const AnalysisResult(this.exitCode, this.errors); + final int exitCode; + final Map> errors; +} + /// Checks samples and code snippets for analysis errors. /// /// Extracts dartdoc content from flutter package source code, identifies code @@ -237,25 +368,6 @@ class SampleChecker { /// generate them. int _expressionId = 0; - /// The exit code from the analysis process. - int _exitCode = 0; - - // Once the snippets tool has been precompiled by Dart, this contains the AOT - // snapshot. - String? _snippetsSnapshotPath; - - /// Finds the location of the snippets script. - String get _snippetsExecutable { - final String platformScriptPath = path.dirname(path.fromUri(Platform.script)); - return path.canonicalize(path.join(platformScriptPath, '..', 'snippets', 'lib', 'main.dart')); - } - - /// Finds the location of the Dart executable. - String get _dartExecutable { - final File dartExecutable = File(Platform.resolvedExecutable); - return dartExecutable.absolute.path; - } - static List _listDartFiles(Directory directory, {bool recursive = false}) { return directory.listSync(recursive: recursive, followLinks: false).whereType().where((File file) => path.extension(file.path) == '.dart').toList(); } @@ -282,9 +394,8 @@ class SampleChecker { List? _headers; /// Checks all the samples in the Dart files in [_flutterPackage] for errors. - int checkSamples() { - _exitCode = 0; - Map> errors = >{}; + Future checkSamples() async { + AnalysisResult? analysisResult; try { final Map sections = {}; final Map snippets = {}; @@ -295,14 +406,14 @@ class SampleChecker { ..._listDartFiles(_flutterPackage, recursive: true), if (_dartUiLocation != null && _dartUiLocation!.existsSync()) ... _listDartFiles(_dartUiLocation!, recursive: true), ]; - _extractSamples(filesToAnalyze, sectionMap: sections, sampleMap: snippets); - errors = _analyze(_tempDirectory, sections, snippets); + await _extractSamples(filesToAnalyze, sectionMap: sections, sampleMap: snippets); + analysisResult = _analyze(_tempDirectory, sections, snippets); } finally { - if (errors.isNotEmpty) { - for (final String filePath in errors.keys) { - errors[filePath]!.forEach(stderr.writeln); + if (analysisResult != null && analysisResult.errors.isNotEmpty) { + for (final String filePath in analysisResult.errors.keys) { + analysisResult.errors[filePath]!.forEach(stderr.writeln); } - stderr.writeln('\nFound ${errors.length} sample code errors.'); + stderr.writeln('\nFound ${analysisResult.errors.length} sample code errors.'); } if (_keepTmp) { print('Leaving temporary directory ${_tempDirectory.path} around for your perusal.'); @@ -313,15 +424,8 @@ class SampleChecker { stderr.writeln('Failed to delete ${_tempDirectory.path}: $e'); } } - // If we made a snapshot, remove it (so as not to clutter up the tree). - if (_snippetsSnapshotPath != null) { - final File snapshot = File(_snippetsSnapshotPath!); - if (snapshot.existsSync()) { - snapshot.deleteSync(); - } - } } - return _exitCode; + return analysisResult.exitCode; } /// Creates a name for the snippets tool to use for the snippet ID from a @@ -333,37 +437,37 @@ class SampleChecker { return sampleId; } - // Precompiles the snippets tool if _snippetsSnapshotPath isn't set yet, and - // runs the precompiled version if it is set. - ProcessResult _runSnippetsScript(List args) { + // The cached JSON Flutter version information from 'flutter --version --machine'. + String? _flutterVersion; + + Future _runSnippetsScript(List args) async { final String workingDirectory = path.join(_flutterRoot, 'dev', 'docs'); - if (_snippetsSnapshotPath == null) { - _snippetsSnapshotPath = '$_snippetsExecutable.snapshot'; - return Process.runSync( - _dartExecutable, - [ - '--snapshot=$_snippetsSnapshotPath', - '--snapshot-kind=app-jit', - path.canonicalize(_snippetsExecutable), - ...args, - ], - workingDirectory: workingDirectory, - ); - } else { - return Process.runSync( - _dartExecutable, - [ - path.canonicalize(_snippetsSnapshotPath!), - ...args, - ], - workingDirectory: workingDirectory, - ); + if (_flutterVersion == null) { + // Capture the flutter version information once so that the snippets tool doesn't + // have to run it for every snippet. + final ProcessResult versionResult = Process.runSync(_flutter, ['--version', '--machine']); + _flutterVersion = versionResult.stdout as String? ?? ''; } + return Process.run( + _pubExecutable, + [ + 'global', + 'run', + 'snippets', + ...args, + ], + workingDirectory: workingDirectory, + environment: { + if (!Platform.environment.containsKey('FLUTTER_ROOT')) 'FLUTTER_ROOT': _flutterRoot, + if (_flutterVersion!.isNotEmpty) 'FLUTTER_VERSION': _flutterVersion!, + }, + includeParentEnvironment: true, + ); } /// Writes out the given sample to an output file in the [_tempDirectory] and /// returns the output file. - File _writeSample(Sample sample) { + Future _writeSample(Sample sample) async { // Generate the snippet. final String sampleId = _createNameFromSource('sample', sample.start.filename, sample.start.line); final String inputName = '$sampleId.input'; @@ -374,11 +478,14 @@ class SampleChecker { final List args = [ '--output=${outputFile.absolute.path}', '--input=${inputFile.absolute.path}', + // Formatting the output will fail on analysis errors, and we want it to fail + // here, not there. + '--no-format-output', ...sample.args, ]; if (verbose) print('Generating sample for ${sample.start.filename}:${sample.start.line}'); - final ProcessResult process = _runSnippetsScript(args); + final ProcessResult process = await _runSnippetsScript(args); if (verbose) stderr.write('${process.stderr}'); if (process.exitCode != 0) { @@ -394,12 +501,12 @@ class SampleChecker { /// Extracts the samples from the Dart files in [files], writes them /// to disk, and adds them to the appropriate [sectionMap] or [sampleMap]. - void _extractSamples( + Future _extractSamples( List files, { required Map sectionMap, required Map sampleMap, bool silent = false, - }) { + }) async { final List
sections =
[]; final List samples = []; int dartpadCount = 0; @@ -545,13 +652,19 @@ class SampleChecker { if (sectionMap != null) sectionMap[path] = section; } + final TaskQueue sampleQueue = TaskQueue(); for (final Sample sample in samples) { - final File snippetFile = _writeSample(sample); + final Future futureFile = sampleQueue.add(() => _writeSample(sample)); if (sampleMap != null) { - sample.contents = snippetFile.readAsLinesSync(); - sampleMap[snippetFile.absolute.path] = sample; + sampleQueue.add(() async { + final File snippetFile = await futureFile; + sample.contents = await snippetFile.readAsLines(); + sampleMap[snippetFile.absolute.path] = sample; + return futureFile; + }); } } + await sampleQueue.tasksComplete; } /// Helper to process arguments given as a (possibly quoted) string. @@ -596,9 +709,11 @@ class SampleChecker { /// Creates the configuration files necessary for the analyzer to consider /// the temporary directory a package, and sets which lint rules to enforce. void _createConfigurationFiles(Directory directory) { - final File pubSpec = File(path.join(directory.path, 'pubspec.yaml'))..createSync(recursive: true); + final File pubSpec = File(path.join(directory.path, 'pubspec.yaml')); + if (!pubSpec.existsSync()) { + pubSpec.createSync(recursive: true); - pubSpec.writeAsStringSync(''' + pubSpec.writeAsStringSync(''' name: analyze_sample_code environment: sdk: ">=2.12.0-0 <3.0.0" @@ -611,11 +726,13 @@ dependencies: dev_dependencies: flutter_lints: ^1.0.3 '''); - + } // Import the analysis options from the Flutter root. final File analysisOptions = File(path.join(directory.path, 'analysis_options.yaml')); - analysisOptions.writeAsStringSync(''' + if (!analysisOptions.existsSync()) { + analysisOptions.createSync(recursive: true); + analysisOptions.writeAsStringSync(''' include: package:flutter_lints/flutter.yaml linter: @@ -623,6 +740,7 @@ linter: # Samples want to print things pretty often. avoid_print: false '''); + } } /// Writes out a sample section to the disk and returns the file. @@ -641,7 +759,7 @@ linter: } /// Invokes the analyzer on the given [directory] and returns the stdout. - List _runAnalyzer(Directory directory, {bool silent = true}) { + int _runAnalyzer(Directory directory, {bool silent = true, required List output}) { if (!silent) print('Starting analysis of code samples.'); _createConfigurationFiles(directory); @@ -654,7 +772,8 @@ linter: final List stdout = result.stdout.toString().trim().split('\n'); // Remove output from building the flutter tool. stderr.removeWhere((String line) { - return line.startsWith('Building flutter tool...'); + return line.startsWith('Building flutter tool...') + || line.startsWith('Waiting for another flutter command to release the startup lock...'); }); // Check out the stderr to see if the analyzer had it's own issues. if (stderr.isNotEmpty && stderr.first.contains(RegExp(r' issues? found\. \(ran in '))) { @@ -672,19 +791,21 @@ linter: if (stdout.isNotEmpty && stdout.first.startsWith('Running "flutter pub get" in ')) { stdout.removeAt(0); } - _exitCode = result.exitCode; - return stdout; + output.addAll(stdout); + return result.exitCode; } /// Starts the analysis phase of checking the samples by invoking the analyzer /// and parsing its output to create a map of filename to [AnalysisError]s. - Map> _analyze( + AnalysisResult _analyze( Directory directory, Map sections, Map samples, { bool silent = false, }) { - final List errors = _runAnalyzer(directory, silent: silent); + final List errors = []; + int exitCode = _runAnalyzer(directory, silent: silent, output: errors); + final Map> analysisErrors = >{}; void addAnalysisError(File file, AnalysisError error) { if (analysisErrors.containsKey(file.path)) { @@ -825,15 +946,15 @@ linter: ); } } - if (_exitCode == 1 && analysisErrors.isEmpty && !unknownAnalyzerErrors) { - _exitCode = 0; + if (exitCode == 1 && analysisErrors.isEmpty && !unknownAnalyzerErrors) { + exitCode = 0; } - if (_exitCode == 0) { + if (exitCode == 0) { if (!silent) print('No analysis errors in samples!'); assert(analysisErrors.isEmpty); } - return analysisErrors; + return AnalysisResult(exitCode, analysisErrors); } /// Process one block of sample code (the part inside of "```" markers). @@ -1070,17 +1191,17 @@ Future _runInteractive({ } print('Starting up in interactive mode on ${path.relative(filePath, from: _flutterRoot)} ...'); - void analyze(SampleChecker checker, File file) { + Future analyze(SampleChecker checker, File file) async { final Map sections = {}; final Map snippets = {}; - checker._extractSamples([file], silent: true, sectionMap: sections, sampleMap: snippets); - final Map> errors = checker._analyze(checker._tempDirectory, sections, snippets, silent: true); + await checker._extractSamples([file], silent: true, sectionMap: sections, sampleMap: snippets); + final AnalysisResult analysisResult = checker._analyze(checker._tempDirectory, sections, snippets, silent: true); stderr.writeln('\u001B[2J\u001B[H'); // Clears the old results from the terminal. - if (errors.isNotEmpty) { - for (final String filePath in errors.keys) { - errors[filePath]!.forEach(stderr.writeln); + if (analysisResult.errors.isNotEmpty) { + for (final String filePath in analysisResult.errors.keys) { + analysisResult.errors[filePath]!.forEach(stderr.writeln); } - stderr.writeln('\nFound ${errors.length} errors.'); + stderr.writeln('\nFound ${analysisResult.errors.length} errors.'); } else { stderr.writeln('\nNo issues found.'); } @@ -1088,7 +1209,7 @@ Future _runInteractive({ final SampleChecker checker = SampleChecker(flutterPackage, tempDirectory: tempDir) .._createConfigurationFiles(tempDir); - analyze(checker, file); + await analyze(checker, file); print('Type "q" to quit, or "r" to delete temp dir and manually reload.'); diff --git a/dev/bots/docs.sh b/dev/bots/docs.sh index 6ef62ccf72f..ad182366500 100755 --- a/dev/bots/docs.sh +++ b/dev/bots/docs.sh @@ -13,14 +13,26 @@ function script_location() { script_location="$(readlink "$script_location")" [[ "$script_location" != /* ]] && script_location="$DIR/$script_location" done - echo "$(cd -P "$(dirname "$script_location")" >/dev/null && pwd)" + cd -P "$(dirname "$script_location")" >/dev/null && pwd } function generate_docs() { # Install and activate dartdoc. # NOTE: When updating to a new dartdoc version, please also update # `dartdoc_options.yaml` to include newly introduced error and warning types. - "$PUB" global activate dartdoc 1.0.0 + "$PUB" global activate dartdoc 1.0.2 + + # Install and activate the snippets tool, which resides in the + # assets-for-api-docs repo: + # https://github.com/flutter/assets-for-api-docs/tree/master/packages/snippets + # >>> If you update this version, also update it in dev/bots/analyze_sample_code.dart <<< + "$PUB" global activate snippets 0.2.2 + + # Install and activate the snippets tool, which resides in the + # assets-for-api-docs repo: + # https://github.com/flutter/assets-for-api-docs/tree/master/packages/snippets + # >>> If you update this version, also update it in dev/bots/analyze_sample_code.dart <<< + "$PUB" global activate snippets 0.2.1 # This script generates a unified doc set, and creates # a custom index.html, placing everything into dev/docs/doc. @@ -96,9 +108,9 @@ function move_offline_into_place() { mv flutter.docs.zip doc/offline/flutter.docs.zip du -sh doc/offline/flutter.docs.zip if [[ "$LUCI_BRANCH" == "stable" ]]; then - echo -e "\n ${FLUTTER_VERSION}\n https://api.flutter.dev/offline/flutter.docset.tar.gz\n" > doc/offline/flutter.xml + echo -e "\n ${FLUTTER_VERSION_STRING}\n https://api.flutter.dev/offline/flutter.docset.tar.gz\n" > doc/offline/flutter.xml else - echo -e "\n ${FLUTTER_VERSION}\n https://master-api.flutter.dev/offline/flutter.docset.tar.gz\n" > doc/offline/flutter.xml + echo -e "\n ${FLUTTER_VERSION_STRING}\n https://master-api.flutter.dev/offline/flutter.docset.tar.gz\n" > doc/offline/flutter.xml fi mv flutter.docset.tar.gz doc/offline/flutter.docset.tar.gz du -sh doc/offline/flutter.docset.tar.gz @@ -125,9 +137,11 @@ DART="$DART_BIN/dart" PUB="$DART_BIN/pub" export PATH="$FLUTTER_BIN:$DART_BIN:$PATH" -# Make sure dart is installed by invoking flutter to download it. -"$FLUTTER" --version -FLUTTER_VERSION=$(cat "$FLUTTER_ROOT/version") +# Make sure dart is installed by invoking Flutter to download it. +# This also creates the 'version' file. +FLUTTER_VERSION=$("$FLUTTER" --version --machine) +export FLUTTER_VERSION +FLUTTER_VERSION_STRING=$(cat "$FLUTTER_ROOT/version") # If the pub cache directory exists in the root, then use that. FLUTTER_PUB_CACHE="$FLUTTER_ROOT/.pub-cache" diff --git a/dev/bots/test.dart b/dev/bots/test.dart index 2c8348d277d..554c4340c5c 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -746,7 +746,6 @@ Future _runFrameworkTests() async { print('${green}Running package tests$reset for directories other than packages/flutter'); await _pubRunTest(path.join(flutterRoot, 'dev', 'bots')); await _pubRunTest(path.join(flutterRoot, 'dev', 'devicelab'), ensurePrecompiledTool: false); // See https://github.com/flutter/flutter/issues/86209 - await _pubRunTest(path.join(flutterRoot, 'dev', 'snippets')); // TODO(fujino): Move this to its own test shard await _pubRunTest(path.join(flutterRoot, 'dev', 'conductor'), forceSingleCore: true); await _runFlutterTest(path.join(flutterRoot, 'dev', 'integration_tests', 'android_semantics_testing')); diff --git a/dev/bots/test/analyze-sample-code-test-dart-ui/ui.dart b/dev/bots/test/analyze-sample-code-test-dart-ui/ui.dart index ba5a55590d4..5b0239d16df 100644 --- a/dev/bots/test/analyze-sample-code-test-dart-ui/ui.dart +++ b/dev/bots/test/analyze-sample-code-test-dart-ui/ui.dart @@ -11,11 +11,13 @@ library dart.ui; /// Annotation used by Flutter's Dart compiler to indicate that an /// [Object.toString] override should not be replaced with a supercall. /// -/// {@tool sample} +/// {@tool sample --template=stateless_widget_material} /// A sample if using keepToString to prevent replacement by a supercall. /// /// ```dart /// class MyStringBuffer { +/// error; +/// /// StringBuffer _buffer = StringBuffer(); /// /// @keepToString diff --git a/dev/bots/test/analyze_sample_code_test.dart b/dev/bots/test/analyze_sample_code_test.dart index 98d90441c01..4f7b32a33ac 100644 --- a/dev/bots/test/analyze_sample_code_test.dart +++ b/dev/bots/test/analyze_sample_code_test.dart @@ -20,11 +20,10 @@ void main() { ['analyze_sample_code.dart', '--no-include-dart-ui', 'test/analyze-sample-code-test-input'], ); final List stdoutLines = process.stdout.toString().split('\n'); - final List stderrLines = process.stderr.toString().split('\n') - ..removeWhere((String line) => line.startsWith('Analyzer output:') || line.startsWith('Building flutter tool...')); + final List stderrLines = process.stderr.toString().split('\n'); expect(process.exitCode, isNot(equals(0))); - expect(stderrLines, [ - 'In sample starting at dev/bots/test/analyze-sample-code-test-input/known_broken_documentation.dart:125: child: Text(title),', + expect(stderrLines, containsAll([ + 'In sample starting at dev/bots/test/analyze-sample-code-test-input/known_broken_documentation.dart:125: child: Text(title),', ">>> error: The final variable 'title' can't be read because it is potentially unassigned at this point (read_potentially_unassigned_final)", 'dev/bots/test/analyze-sample-code-test-input/known_broken_documentation.dart:30:9: new Opacity(', '>>> info: Unnecessary new keyword (unnecessary_new)', @@ -38,32 +37,35 @@ void main() { ">>> error: A value of type 'Null' can't be assigned to a variable of type 'int' (invalid_assignment)", 'dev/bots/test/analyze-sample-code-test-input/known_broken_documentation.dart:120:24: const SizedBox(),', '>>> error: Unexpected comma at end of sample code. (missing_identifier)', - '', 'Found 2 sample code errors.', - '' - ]); - expect(stdoutLines, [ + ])); + expect(stdoutLines, containsAll([ 'Found 9 snippet code blocks, 0 sample code sections, and 2 dartpad sections.', 'Starting analysis of code samples.', - '', - ]); + ])); }); test('Analyzes dart:ui code', () { final ProcessResult process = Process.runSync( '../../bin/cache/dart-sdk/bin/dart', [ 'analyze_sample_code.dart', - '--dart-ui-location', - 'test/analyze-sample-code-test-dart-ui', + '--dart-ui-location=test/analyze-sample-code-test-dart-ui', 'test/analyze-sample-code-test-input', ], ); final List stdoutLines = process.stdout.toString().split('\n'); + final List stderrLines = process.stderr.toString().split('\n'); expect(process.exitCode, isNot(equals(0))); - expect(stdoutLines, equals([ + expect(stderrLines, containsAll([ + 'In sample starting at dev/bots/test/analyze-sample-code-test-dart-ui/ui.dart:15:class MyStatelessWidget extends StatelessWidget {', + ">>> error: Missing concrete implementation of 'StatelessWidget.build' (non_abstract_class_inherits_abstract_member)", + 'In sample starting at dev/bots/test/analyze-sample-code-test-dart-ui/ui.dart:15:class MyStringBuffer {', + ">>> error: Classes can't be declared inside other classes (class_in_class)", + ])); + expect(stdoutLines, containsAll([ // There is one sample code section in the test's dummy dart:ui code. 'Found 9 snippet code blocks, 1 sample code sections, and 2 dartpad sections.', - '', + 'Starting analysis of code samples.', ])); }); } diff --git a/dev/snippets/config/skeletons/dartpad-sample.html b/dev/snippets/config/skeletons/dartpad-sample.html index ad850a084a8..8cbd443846d 100644 --- a/dev/snippets/config/skeletons/dartpad-sample.html +++ b/dev/snippets/config/skeletons/dartpad-sample.html @@ -8,18 +8,6 @@ link -
- - - -
{{description}} @@ -28,15 +16,5 @@ flutter create --sample={{id}} mysample
- {@end-inject-html} diff --git a/dev/snippets/config/skeletons/snippet.html b/dev/snippets/config/skeletons/snippet.html index 75f87824e4e..99574b028b8 100644 --- a/dev/snippets/config/skeletons/snippet.html +++ b/dev/snippets/config/skeletons/snippet.html @@ -8,9 +8,6 @@ link -
- -
{{description}}
diff --git a/dev/snippets/config/templates/freeform.tmpl b/dev/snippets/config/templates/freeform.tmpl index 1337bd4b585..ea4f7f08180 100644 --- a/dev/snippets/config/templates/freeform.tmpl +++ b/dev/snippets/config/templates/freeform.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} diff --git a/dev/snippets/config/templates/stateful_widget.tmpl b/dev/snippets/config/templates/stateful_widget.tmpl index db4472249f8..014f618e6d1 100644 --- a/dev/snippets/config/templates/stateful_widget.tmpl +++ b/dev/snippets/config/templates/stateful_widget.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -35,5 +35,5 @@ class MyStatefulWidget extends StatefulWidget { /// This is the private State class that goes with MyStatefulWidget. class _MyStatefulWidgetState extends State { - {{code}} +{{code}} } diff --git a/dev/snippets/config/templates/stateful_widget_cupertino.tmpl b/dev/snippets/config/templates/stateful_widget_cupertino.tmpl index a07430d9eb3..e424cddcb3c 100644 --- a/dev/snippets/config/templates/stateful_widget_cupertino.tmpl +++ b/dev/snippets/config/templates/stateful_widget_cupertino.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -36,5 +36,5 @@ class MyStatefulWidget extends StatefulWidget { /// This is the private State class that goes with MyStatefulWidget. class _MyStatefulWidgetState extends State { - {{code}} +{{code}} } diff --git a/dev/snippets/config/templates/stateful_widget_cupertino_page_scaffold.tmpl b/dev/snippets/config/templates/stateful_widget_cupertino_page_scaffold.tmpl index 509b2558b0b..905f4da4d5c 100644 --- a/dev/snippets/config/templates/stateful_widget_cupertino_page_scaffold.tmpl +++ b/dev/snippets/config/templates/stateful_widget_cupertino_page_scaffold.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -39,5 +39,5 @@ class MyStatefulWidget extends StatefulWidget { /// This is the private State class that goes with MyStatefulWidget. class _MyStatefulWidgetState extends State { - {{code}} +{{code}} } diff --git a/dev/snippets/config/templates/stateful_widget_cupertino_ticker.tmpl b/dev/snippets/config/templates/stateful_widget_cupertino_ticker.tmpl index 32262d67fe8..38c75479831 100644 --- a/dev/snippets/config/templates/stateful_widget_cupertino_ticker.tmpl +++ b/dev/snippets/config/templates/stateful_widget_cupertino_ticker.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -37,5 +37,5 @@ class MyStatefulWidget extends StatefulWidget { /// This is the private State class that goes with MyStatefulWidget. /// AnimationControllers can be created with `vsync: this` because of TickerProviderStateMixin. class _MyStatefulWidgetState extends State with TickerProviderStateMixin { - {{code}} +{{code}} } diff --git a/dev/snippets/config/templates/stateful_widget_material.tmpl b/dev/snippets/config/templates/stateful_widget_material.tmpl index 233bbc9769c..fe916062875 100644 --- a/dev/snippets/config/templates/stateful_widget_material.tmpl +++ b/dev/snippets/config/templates/stateful_widget_material.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -36,5 +36,5 @@ class MyStatefulWidget extends StatefulWidget { /// This is the private State class that goes with MyStatefulWidget. class _MyStatefulWidgetState extends State { - {{code}} +{{code}} } diff --git a/dev/snippets/config/templates/stateful_widget_material_ticker.tmpl b/dev/snippets/config/templates/stateful_widget_material_ticker.tmpl index 02f2e2047e3..0357ff41a8b 100644 --- a/dev/snippets/config/templates/stateful_widget_material_ticker.tmpl +++ b/dev/snippets/config/templates/stateful_widget_material_ticker.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -37,5 +37,5 @@ class MyStatefulWidget extends StatefulWidget { /// This is the private State class that goes with MyStatefulWidget. /// AnimationControllers can be created with `vsync: this` because of TickerProviderStateMixin. class _MyStatefulWidgetState extends State with TickerProviderStateMixin { - {{code}} +{{code}} } diff --git a/dev/snippets/config/templates/stateful_widget_restoration.tmpl b/dev/snippets/config/templates/stateful_widget_restoration.tmpl index 05c7c1ba354..405a9b6629c 100644 --- a/dev/snippets/config/templates/stateful_widget_restoration.tmpl +++ b/dev/snippets/config/templates/stateful_widget_restoration.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} diff --git a/dev/snippets/config/templates/stateful_widget_restoration_cupertino.tmpl b/dev/snippets/config/templates/stateful_widget_restoration_cupertino.tmpl index d7267f3d2d2..3435e8c2a00 100644 --- a/dev/snippets/config/templates/stateful_widget_restoration_cupertino.tmpl +++ b/dev/snippets/config/templates/stateful_widget_restoration_cupertino.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} diff --git a/dev/snippets/config/templates/stateful_widget_restoration_material.tmpl b/dev/snippets/config/templates/stateful_widget_restoration_material.tmpl index 5d3fc3c95bd..11045ab5926 100644 --- a/dev/snippets/config/templates/stateful_widget_restoration_material.tmpl +++ b/dev/snippets/config/templates/stateful_widget_restoration_material.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} diff --git a/dev/snippets/config/templates/stateful_widget_scaffold.tmpl b/dev/snippets/config/templates/stateful_widget_scaffold.tmpl index b7b6ccb542d..bfc77b006e7 100644 --- a/dev/snippets/config/templates/stateful_widget_scaffold.tmpl +++ b/dev/snippets/config/templates/stateful_widget_scaffold.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -39,5 +39,5 @@ class MyStatefulWidget extends StatefulWidget { /// This is the private State class that goes with MyStatefulWidget. class _MyStatefulWidgetState extends State { - {{code}} +{{code}} } diff --git a/dev/snippets/config/templates/stateful_widget_scaffold_center.tmpl b/dev/snippets/config/templates/stateful_widget_scaffold_center.tmpl index 16191373dfb..bd6bc66b512 100644 --- a/dev/snippets/config/templates/stateful_widget_scaffold_center.tmpl +++ b/dev/snippets/config/templates/stateful_widget_scaffold_center.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -41,5 +41,5 @@ class MyStatefulWidget extends StatefulWidget { /// This is the private State class that goes with MyStatefulWidget. class _MyStatefulWidgetState extends State { - {{code}} +{{code}} } diff --git a/dev/snippets/config/templates/stateful_widget_scaffold_center_freeform_state.tmpl b/dev/snippets/config/templates/stateful_widget_scaffold_center_freeform_state.tmpl index 4f05456dea1..1659319c4f3 100644 --- a/dev/snippets/config/templates/stateful_widget_scaffold_center_freeform_state.tmpl +++ b/dev/snippets/config/templates/stateful_widget_scaffold_center_freeform_state.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} diff --git a/dev/snippets/config/templates/stateful_widget_ticker.tmpl b/dev/snippets/config/templates/stateful_widget_ticker.tmpl index de5daeacf09..ced2116ed56 100644 --- a/dev/snippets/config/templates/stateful_widget_ticker.tmpl +++ b/dev/snippets/config/templates/stateful_widget_ticker.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -36,5 +36,5 @@ class MyStatefulWidget extends StatefulWidget { /// This is the private State class that goes with MyStatefulWidget. /// AnimationControllers can be created with `vsync: this` because of TickerProviderStateMixin. class _MyStatefulWidgetState extends State with TickerProviderStateMixin { - {{code}} +{{code}} } diff --git a/dev/snippets/config/templates/stateless_widget.tmpl b/dev/snippets/config/templates/stateless_widget.tmpl index fa5c4a848d1..c04e577423c 100644 --- a/dev/snippets/config/templates/stateless_widget.tmpl +++ b/dev/snippets/config/templates/stateless_widget.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -30,5 +30,5 @@ class MyStatelessWidget extends StatelessWidget { const MyStatelessWidget({Key? key}) : super(key: key); @override - {{code}} +{{code}} } diff --git a/dev/snippets/config/templates/stateless_widget_cupertino.tmpl b/dev/snippets/config/templates/stateless_widget_cupertino.tmpl index 7d116d09584..9eec7767482 100644 --- a/dev/snippets/config/templates/stateless_widget_cupertino.tmpl +++ b/dev/snippets/config/templates/stateless_widget_cupertino.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -32,5 +32,5 @@ class MyStatelessWidget extends StatelessWidget { const MyStatelessWidget({Key? key}) : super(key: key); @override - {{code}} +{{code}} } diff --git a/dev/snippets/config/templates/stateless_widget_cupertino_page_scaffold.tmpl b/dev/snippets/config/templates/stateless_widget_cupertino_page_scaffold.tmpl index ffa71d0784a..dd4552a82a7 100644 --- a/dev/snippets/config/templates/stateless_widget_cupertino_page_scaffold.tmpl +++ b/dev/snippets/config/templates/stateless_widget_cupertino_page_scaffold.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -35,5 +35,5 @@ class MyStatelessWidget extends StatelessWidget { const MyStatelessWidget({Key? key}) : super(key: key); @override - {{code}} +{{code}} } diff --git a/dev/snippets/config/templates/stateless_widget_material.tmpl b/dev/snippets/config/templates/stateless_widget_material.tmpl index ef408dfd209..dc5e7a3c6dd 100644 --- a/dev/snippets/config/templates/stateless_widget_material.tmpl +++ b/dev/snippets/config/templates/stateless_widget_material.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -32,5 +32,5 @@ class MyStatelessWidget extends StatelessWidget { const MyStatelessWidget({Key? key}) : super(key: key); @override - {{code}} +{{code}} } diff --git a/dev/snippets/config/templates/stateless_widget_restoration_cupertino.tmpl b/dev/snippets/config/templates/stateless_widget_restoration_cupertino.tmpl index 5e2a6e13c1b..975654ac96d 100644 --- a/dev/snippets/config/templates/stateless_widget_restoration_cupertino.tmpl +++ b/dev/snippets/config/templates/stateless_widget_restoration_cupertino.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -32,5 +32,5 @@ class MyStatelessWidget extends StatelessWidget { const MyStatelessWidget({Key? key}) : super(key: key); @override - {{code}} +{{code}} } diff --git a/dev/snippets/config/templates/stateless_widget_restoration_material.tmpl b/dev/snippets/config/templates/stateless_widget_restoration_material.tmpl index 2d936ef3478..5e34280dbd1 100644 --- a/dev/snippets/config/templates/stateless_widget_restoration_material.tmpl +++ b/dev/snippets/config/templates/stateless_widget_restoration_material.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -32,5 +32,5 @@ class MyStatelessWidget extends StatelessWidget { const MyStatelessWidget({Key? key}) : super(key: key); @override - {{code}} +{{code}} } diff --git a/dev/snippets/config/templates/stateless_widget_scaffold.tmpl b/dev/snippets/config/templates/stateless_widget_scaffold.tmpl index a79807efe05..834656e3487 100644 --- a/dev/snippets/config/templates/stateless_widget_scaffold.tmpl +++ b/dev/snippets/config/templates/stateless_widget_scaffold.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -35,5 +35,5 @@ class MyStatelessWidget extends StatelessWidget { const MyStatelessWidget({Key? key}) : super(key: key); @override - {{code}} +{{code}} } diff --git a/dev/snippets/config/templates/stateless_widget_scaffold_center.tmpl b/dev/snippets/config/templates/stateless_widget_scaffold_center.tmpl index f3d24395894..85eb7a56ed1 100644 --- a/dev/snippets/config/templates/stateless_widget_scaffold_center.tmpl +++ b/dev/snippets/config/templates/stateless_widget_scaffold_center.tmpl @@ -1,5 +1,5 @@ -/// Flutter code sample for {{element}} - +// Flutter code sample for {{element}} +// {{description}} {{code-dartImports}} @@ -37,5 +37,5 @@ class MyStatelessWidget extends StatelessWidget { const MyStatelessWidget({Key? key}) : super(key: key); @override - {{code}} +{{code}} } diff --git a/dev/snippets/lib/configuration.dart b/dev/snippets/lib/configuration.dart deleted file mode 100644 index d3729ba4c22..00000000000 --- a/dev/snippets/lib/configuration.dart +++ /dev/null @@ -1,80 +0,0 @@ -// 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:io' hide Platform; - -import 'package:meta/meta.dart'; -import 'package:path/path.dart' as path; - -/// What type of snippet to produce. -enum SnippetType { - /// Produces a snippet that includes the code interpolated into an application - /// template. - sample, - - /// Produces a nicely formatted sample code, but no application. - snippet, -} - -/// Return the name of an enum item. -String getEnumName(dynamic enumItem) { - final String name = '$enumItem'; - final int index = name.indexOf('.'); - return index == -1 ? name : name.substring(index + 1); -} - -/// A class to compute the configuration of the snippets input and output -/// locations based in the current location of the snippets main.dart. -class Configuration { - Configuration({required this.flutterRoot}) : assert(flutterRoot != null); - - final Directory flutterRoot; - - /// This is the configuration directory for the snippets system, containing - /// the skeletons and templates. - @visibleForTesting - Directory get configDirectory { - _configPath ??= Directory( - path.canonicalize(path.join(flutterRoot.absolute.path, 'dev', 'snippets', 'config'))); - return _configPath!; - } - - // Nullable so that we can use it as a lazy cache. - Directory? _configPath; - - /// This is where the snippets themselves will be written, in order to be - /// uploaded to the docs site. - Directory get outputDirectory { - _docsDirectory ??= Directory( - path.canonicalize(path.join(flutterRoot.absolute.path, 'dev', 'docs', 'doc', 'snippets'))); - return _docsDirectory!; - } - - // Nullable so that we can use it as a lazy cache. - Directory? _docsDirectory; - - /// This makes sure that the output directory exists. - void createOutputDirectory() { - if (!outputDirectory.existsSync()) { - outputDirectory.createSync(recursive: true); - } - } - - /// The directory containing the HTML skeletons to be filled out with metadata - /// and returned to dartdoc for insertion in the output. - Directory get skeletonsDirectory => Directory(path.join(configDirectory.path,'skeletons')); - - /// The directory containing the code templates that can be referenced by the - /// dartdoc. - Directory get templatesDirectory => Directory(path.join(configDirectory.path, 'templates')); - - /// Gets the skeleton file to use for the given [SnippetType] and DartPad preference. - File getHtmlSkeletonFile(SnippetType type, {bool showDartPad = false}) { - assert(!showDartPad || type == SnippetType.sample, - 'Only application snippets work with dartpad.'); - final String filename = - '${showDartPad ? 'dartpad-' : ''}${getEnumName(type)}.html'; - return File(path.join(skeletonsDirectory.path, filename)); - } -} diff --git a/dev/snippets/lib/main.dart b/dev/snippets/lib/main.dart deleted file mode 100644 index 6fa01276980..00000000000 --- a/dev/snippets/lib/main.dart +++ /dev/null @@ -1,241 +0,0 @@ -// 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:io' show exit, stderr, stdout, File, ProcessResult; - -import 'package:args/args.dart'; -import 'package:meta/meta.dart'; -import 'package:path/path.dart' as path; -import 'package:platform/platform.dart'; -import 'package:process/process.dart'; - -import 'configuration.dart'; -import 'snippets.dart'; - -const String _kSerialOption = 'serial'; -const String _kElementOption = 'element'; -const String _kHelpOption = 'help'; -const String _kInputOption = 'input'; -const String _kLibraryOption = 'library'; -const String _kOutputOption = 'output'; -const String _kPackageOption = 'package'; -const String _kTemplateOption = 'template'; -const String _kTypeOption = 'type'; -const String _kShowDartPad = 'dartpad'; - -class GitStatusFailed implements Exception { - GitStatusFailed(this.gitResult); - - final ProcessResult gitResult; - - @override - String toString() => 'git status exited with a non-zero exit code: ${gitResult.exitCode}:\n${gitResult.stderr}\n${gitResult.stdout}'; -} - -/// Get the name of the channel these docs are from. -/// -/// First check env variable LUCI_BRANCH, then refer to the currently -/// checked out git branch. -String getChannelName({ - @visibleForTesting - Platform platform = const LocalPlatform(), - @visibleForTesting - ProcessManager processManager = const LocalProcessManager(), -}) { - final String? envReleaseChannel = platform.environment['LUCI_BRANCH']?.trim(); - if (['master', 'stable'].contains(envReleaseChannel)) { - return envReleaseChannel!; - } - final RegExp gitBranchRegexp = RegExp(r'^## (?.*)'); - final ProcessResult gitResult = processManager.runSync(['git', 'status', '-b', '--porcelain'], - environment: { - 'GIT_TRACE': '2', - 'GIT_TRACE_SETUP': '2' - }, - includeParentEnvironment: true - ); - if (gitResult.exitCode != 0) { - throw GitStatusFailed(gitResult); - } - final RegExpMatch? gitBranchMatch = gitBranchRegexp.firstMatch((gitResult.stdout as String).trim().split('\n').first); - return gitBranchMatch == null ? '' : gitBranchMatch.namedGroup('branch')!.split('...').first; -} - -// This is a hack to workaround the fact that git status inexplicably fails -// (random non-zero error code) about 2% of the time. -String getChannelNameWithRetries() { - int retryCount = 0; - while(retryCount < 2) { - try { - return getChannelName(); - } on GitStatusFailed catch (e) { - retryCount += 1; - stderr.write('git status failed, retrying ($retryCount)\nError report:\n$e'); - } - } - return getChannelName(); -} - -/// Generates snippet dartdoc output for a given input, and creates any sample -/// applications needed by the snippet. -void main(List argList) { - const Platform platform = LocalPlatform(); - final Map environment = platform.environment; - final ArgParser parser = ArgParser(); - final List snippetTypes = - SnippetType.values.map((SnippetType type) => getEnumName(type)).toList(); - parser.addOption( - _kTypeOption, - defaultsTo: getEnumName(SnippetType.sample), - allowed: snippetTypes, - allowedHelp: { - getEnumName(SnippetType.sample): - 'Produce a code sample application complete with embedding the sample in an ' - 'application template.', - getEnumName(SnippetType.snippet): - 'Produce a nicely formatted piece of sample code. Does not embed the ' - 'sample into an application template.', - }, - help: 'The type of snippet to produce.', - ); - parser.addOption( - _kTemplateOption, - defaultsTo: null, - help: 'The name of the template to inject the code into.', - ); - parser.addOption( - _kOutputOption, - defaultsTo: null, - help: 'The output path for the generated sample application. Overrides ' - 'the naming generated by the --package/--library/--element arguments. ' - 'Metadata will be written alongside in a .json file. ' - 'The basename of this argument is used as the ID', - ); - parser.addOption( - _kInputOption, - defaultsTo: environment['INPUT'], - help: 'The input file containing the sample code to inject.', - ); - parser.addOption( - _kPackageOption, - defaultsTo: environment['PACKAGE_NAME'], - help: 'The name of the package that this sample belongs to.', - ); - parser.addOption( - _kLibraryOption, - defaultsTo: environment['LIBRARY_NAME'], - help: 'The name of the library that this sample belongs to.', - ); - parser.addOption( - _kElementOption, - defaultsTo: environment['ELEMENT_NAME'], - help: 'The name of the element that this sample belongs to.', - ); - parser.addOption( - _kSerialOption, - defaultsTo: environment['INVOCATION_INDEX'], - help: 'A unique serial number for this snippet tool invocation.', - ); - parser.addFlag( - _kHelpOption, - defaultsTo: false, - negatable: false, - help: 'Prints help documentation for this command', - ); - parser.addFlag( - _kShowDartPad, - defaultsTo: false, - negatable: false, - help: "Indicates whether DartPad should be included in the sample's " - 'final HTML output. This flag only applies when the type parameter is ' - '"sample".', - ); - - final ArgResults args = parser.parse(argList); - - if (args[_kHelpOption] as bool) { - stderr.writeln(parser.usage); - exit(0); - } - - final SnippetType snippetType = SnippetType.values - .firstWhere((SnippetType type) => getEnumName(type) == args[_kTypeOption]); - - if (args[_kShowDartPad] == true && snippetType != SnippetType.sample) { - errorExit('${args[_kTypeOption]} was selected, but the --dartpad flag is only valid ' - 'for application sample code.'); - } - - if (args[_kInputOption] == null) { - stderr.writeln(parser.usage); - errorExit('The --$_kInputOption option must be specified, either on the command ' - 'line, or in the INPUT environment variable.'); - } - - final File input = File(args['input'] as String); - if (!input.existsSync()) { - errorExit('The input file ${input.path} does not exist.'); - } - - String? template; - if (snippetType == SnippetType.sample) { - final String templateArg = args[_kTemplateOption] as String; - if (templateArg == null || templateArg.isEmpty) { - stderr.writeln(parser.usage); - errorExit('The --$_kTemplateOption option must be specified on the command ' - 'line for application samples.'); - } - template = templateArg.replaceAll(RegExp(r'.tmpl$'), ''); - } - - final String packageName = args[_kPackageOption] as String? ?? ''; - final String libraryName = args[_kLibraryOption] as String? ?? ''; - final String elementName = args[_kElementOption] as String? ?? ''; - final String serial = args[_kSerialOption] as String? ?? ''; - final List id = []; - if (args[_kOutputOption] != null) { - id.add(path.basename(path.basenameWithoutExtension(args[_kOutputOption] as String))); - } else { - if (packageName.isNotEmpty && packageName != 'flutter') { - id.add(packageName); - } - if (libraryName.isNotEmpty) { - id.add(libraryName); - } - if (elementName.isNotEmpty) { - id.add(elementName); - } - if (serial.isNotEmpty) { - id.add(serial); - } - if (id.isEmpty) { - errorExit('Unable to determine ID. At least one of --$_kPackageOption, ' - '--$_kLibraryOption, --$_kElementOption, -$_kSerialOption, or the environment variables ' - 'PACKAGE_NAME, LIBRARY_NAME, ELEMENT_NAME, or INVOCATION_INDEX must be non-empty.'); - } - } - - final SnippetGenerator generator = SnippetGenerator(); - stdout.write(generator.generate( - input, - snippetType, - showDartPad: args[_kShowDartPad] as bool, - template: template, - output: args[_kOutputOption] != null ? File(args[_kOutputOption] as String) : null, - metadata: { - 'sourcePath': environment['SOURCE_PATH'], - 'sourceLine': environment['SOURCE_LINE'] != null - ? int.tryParse(environment['SOURCE_LINE']!) - : null, - 'id': id.join('.'), - 'channel': getChannelNameWithRetries(), - 'serial': serial, - 'package': packageName, - 'library': libraryName, - 'element': elementName, - }, - )); - - exit(0); -} diff --git a/dev/snippets/lib/snippets.dart b/dev/snippets/lib/snippets.dart deleted file mode 100644 index 2dddca0b810..00000000000 --- a/dev/snippets/lib/snippets.dart +++ /dev/null @@ -1,350 +0,0 @@ -// 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:convert'; -import 'dart:io'; - -import 'package:dart_style/dart_style.dart'; -import 'package:path/path.dart' as path; - -import 'configuration.dart'; - -void errorExit(String message) { - stderr.writeln(message); - exit(1); -} - -// A Tuple containing the name and contents associated with a code block in a -// snippet. -class _ComponentTuple { - _ComponentTuple(this.name, this.contents, {this.language = ''}); - final String name; - final List contents; - final String language; - String get mergedContent => contents.join('\n').trim(); -} - -/// Generates the snippet HTML, as well as saving the output snippet main to -/// the output directory. -class SnippetGenerator { - SnippetGenerator({Configuration? configuration}) - : configuration = configuration ?? - // Flutter's root is four directories up from this script. - Configuration(flutterRoot: Directory(Platform.environment['FLUTTER_ROOT'] - ?? path.canonicalize(path.join(path.dirname(path.fromUri(Platform.script)), '..', '..', '..')))) { - this.configuration.createOutputDirectory(); - } - - /// The configuration used to determine where to get/save data for the - /// snippet. - final Configuration configuration; - - static const JsonEncoder jsonEncoder = JsonEncoder.withIndent(' '); - - /// A Dart formatted used to format the snippet code and finished application - /// code. - static DartFormatter formatter = DartFormatter(pageWidth: 80, fixes: StyleFix.all); - - /// This returns the output file for a given snippet ID. Only used for - /// [SnippetType.sample] snippets. - File getOutputFile(String id) => File(path.join(configuration.outputDirectory.path, '$id.dart')); - - /// Gets the path to the template file requested. - File? getTemplatePath(String templateName, {Directory? templatesDir}) { - final Directory templateDir = templatesDir ?? configuration.templatesDirectory; - final File templateFile = File(path.join(templateDir.path, '$templateName.tmpl')); - return templateFile.existsSync() ? templateFile : null; - } - - /// Injects the [injections] into the [template], and turning the - /// "description" injection into a comment. Only used for - /// [SnippetType.sample] snippets. - String _interpolateTemplate(List<_ComponentTuple> injections, String template, Map metadata) { - final RegExp moustacheRegExp = RegExp('{{([^}]+)}}'); - final String interpolated = template.replaceAllMapped(moustacheRegExp, (Match match) { - if (match[1] == 'description') { - // Place the description into a comment. - final List description = injections - .firstWhere((_ComponentTuple tuple) => tuple.name == match[1]) - .contents - .map((String line) => '// $line') - .toList(); - // Remove any leading/trailing empty comment lines. - // We don't want to remove ALL empty comment lines, only the ones at the - // beginning and the end. - while (description.isNotEmpty && description.last == '// ') { - description.removeLast(); - } - while (description.isNotEmpty && description.first == '// ') { - description.removeAt(0); - } - return description.join('\n').trim(); - } else { - // If the match isn't found in the injections, then just remove the - // mustache reference, since we want to allow the sections to be - // "optional" in the input: users shouldn't be forced to add an empty - // "```dart preamble" section if that section would be empty. - final int componentIndex = injections - .indexWhere((_ComponentTuple tuple) => tuple.name == match[1]); - if (componentIndex == -1) { - return (metadata[match[1]] ?? '').toString(); - } - return injections[componentIndex].mergedContent; - } - }).trim(); - return _sortImports(interpolated); - } - - String _sortImports(String code) { - final List result = []; - final List lines = code.split('\n'); - final List imports = []; - int firstImport = -1; - int lineNumber =0; - for (final String line in lines) { - if (RegExp(r'^\s*import').matchAsPrefix(line) != null) { - if (firstImport < 0) { - firstImport = lineNumber; - } - imports.add(line); - } else { - result.add(line); - } - lineNumber++; - } - if (firstImport > 0) { - final List dartImports = []; - final List packageImports = []; - final List otherImports = []; - final RegExp typeRegExp = RegExp(r'''import\s+['"](?\w+)'''); - imports.sort(); - for (final String importLine in imports) { - final RegExpMatch? match = typeRegExp.firstMatch(importLine); - if (match != null) { - switch (match.namedGroup('type')) { - case 'dart': - dartImports.add(importLine); - break; - case 'package': - packageImports.add(importLine); - break; - default: - otherImports.add(importLine); - break; - } - } else { - otherImports.add(importLine); - } - } - - // Insert the sorted sections in the proper order, with a blank line in between - // sections. - result.insertAll(firstImport, [ - ...dartImports, - if (dartImports.isNotEmpty) '', - ...packageImports, - if (packageImports.isNotEmpty) '', - ...otherImports, - ]); - } - return result.join('\n'); - } - - /// Interpolates the [injections] into an HTML skeleton file. - /// - /// Similar to interpolateTemplate, but we are only looking for `code-` - /// components, and we care about the order of the injections. - /// - /// Takes into account the [type] and doesn't substitute in the id and the app - /// if not a [SnippetType.sample] snippet. - String _interpolateSkeleton( - SnippetType type, - List<_ComponentTuple> injections, - String skeleton, - Map metadata, - ) { - final List result = []; - const HtmlEscape htmlEscape = HtmlEscape(); - String language = 'dart'; - for (final _ComponentTuple injection in injections) { - if (!injection.name.startsWith('code')) { - continue; - } - result.addAll(injection.contents); - if (injection.language.isNotEmpty) { - language = injection.language; - } - result.addAll(['', '// ...', '']); - } - if (result.length > 3) { - result.removeRange(result.length - 3, result.length); - } - // Only insert a div for the description if there actually is some text there. - // This means that the {{description}} marker in the skeleton needs to - // be inside of an {@inject-html} block. - String description = injections.firstWhere((_ComponentTuple tuple) => tuple.name == 'description').mergedContent; - description = description.trim().isNotEmpty - ? '
{@end-inject-html}$description{@inject-html}
' - : ''; - - // DartPad only supports stable or master as valid channels. Use master - // if not on stable so that local runs will work (although they will - // still take their sample code from the master docs server). - final String channel = metadata['channel'] == 'stable' ? 'stable' : 'master'; - - final Map substitutions = { - 'description': description, - 'code': htmlEscape.convert(result.join('\n')), - 'language': language, - 'serial': '', - 'id': metadata['id']! as String, - 'channel': channel, - 'element': (metadata['element'] ?? '') as String, - 'app': '', - }; - if (type == SnippetType.sample) { - substitutions - ..['serial'] = metadata['serial']?.toString() ?? '0' - ..['app'] = htmlEscape.convert(injections.firstWhere((_ComponentTuple tuple) => tuple.name == 'app').mergedContent); - } - return skeleton.replaceAllMapped(RegExp('{{(${substitutions.keys.join('|')})}}'), (Match match) { - return substitutions[match[1]] ?? ''; - }); - } - - /// Parses the input for the various code and description segments, and - /// returns them in the order found. - List<_ComponentTuple> _parseInput(String input) { - bool inCodeBlock = false; - input = input.trim(); - final List description = []; - final List<_ComponentTuple> components = <_ComponentTuple>[]; - String? language; - final RegExp codeStartEnd = RegExp(r'^\s*```(?[-\w]+|[-\w]+ (?
[-\w]+))?\s*$'); - for (final String line in input.split('\n')) { - final RegExpMatch? match = codeStartEnd.firstMatch(line); - if (match != null) { // If we saw the start or end of a code block - inCodeBlock = !inCodeBlock; - if (match.namedGroup('language') != null) { - language = match[1]; - assert(language != null); - language = language!; - if (match.namedGroup('section') != null) { - components.add(_ComponentTuple('code-${match.namedGroup('section')}', [], language: language)); - } else { - components.add(_ComponentTuple('code', [], language: language)); - } - } else { - language = null; - } - continue; - } - if (!inCodeBlock) { - description.add(line); - } else { - assert(language != null); - if (components.isNotEmpty) { - components.last.contents.add(line); - } - } - } - return <_ComponentTuple>[ - _ComponentTuple('description', description), - ...components, - ]; - } - - String _loadFileAsUtf8(File file) { - return file.readAsStringSync(encoding: utf8); - } - - String _addLineNumbers(String app) { - final StringBuffer buffer = StringBuffer(); - int count = 0; - for (final String line in app.split('\n')) { - count++; - buffer.writeln('${count.toString().padLeft(5, ' ')}: $line'); - } - return buffer.toString(); - } - - /// The main routine for generating snippets. - /// - /// The [input] is the file containing the dartdoc comments (minus the leading - /// comment markers). - /// - /// The [type] is the type of snippet to create: either a - /// [SnippetType.sample] or a [SnippetType.snippet]. - /// - /// [showDartPad] indicates whether DartPad should be shown where possible. - /// Currently, this value only has an effect if [type] is - /// [SnippetType.sample], in which case an alternate skeleton file is - /// used to create the final HTML output. - /// - /// The [template] must not be null if the [type] is - /// [SnippetType.sample], and specifies the name of the template to use - /// for the application code. - /// - /// The [id] is a string ID to use for the output file, and to tell the user - /// about in the `flutter create` hint. It must not be null if the [type] is - /// [SnippetType.sample]. - String generate( - File input, - SnippetType type, { - bool showDartPad = false, - String? template, - File? output, - required Map metadata, - }) { - assert(template != null || type != SnippetType.sample); - assert(metadata['id'] != null); - assert(!showDartPad || type == SnippetType.sample, 'Only application samples work with dartpad.'); - final List<_ComponentTuple> snippetData = _parseInput(_loadFileAsUtf8(input)); - switch (type) { - case SnippetType.sample: - final Directory templatesDir = configuration.templatesDirectory; - if (templatesDir == null) { - stderr.writeln('Unable to find the templates directory.'); - exit(1); - } - final File? templateFile = getTemplatePath(template!, templatesDir: templatesDir); - if (templateFile == null) { - stderr.writeln('The template $template was not found in the templates directory ${templatesDir.path}'); - exit(1); - } - final String templateContents = _loadFileAsUtf8(templateFile); - String app = _interpolateTemplate(snippetData, templateContents, metadata); - - try { - app = formatter.format(app); - } on FormatterException catch (exception) { - stderr.write('Code to format:\n${_addLineNumbers(app)}\n'); - errorExit('Unable to format snippet app template: $exception'); - } - - snippetData.add(_ComponentTuple('app', app.split('\n'))); - final File outputFile = output ?? getOutputFile(metadata['id']! as String); - stderr.writeln('Writing to ${outputFile.absolute.path}'); - outputFile.writeAsStringSync(app); - - final File metadataFile = File(path.join(path.dirname(outputFile.path), - '${path.basenameWithoutExtension(outputFile.path)}.json')); - stderr.writeln('Writing metadata to ${metadataFile.absolute.path}'); - final int descriptionIndex = snippetData.indexWhere( - (_ComponentTuple data) => data.name == 'description'); - final String descriptionString = descriptionIndex == -1 ? '' : snippetData[descriptionIndex].mergedContent; - metadata.addAll({ - 'file': path.basename(outputFile.path), - 'description': descriptionString, - }); - metadataFile.writeAsStringSync(jsonEncoder.convert(metadata)); - break; - case SnippetType.snippet: - break; - } - final String skeleton = - _loadFileAsUtf8(configuration.getHtmlSkeletonFile(type, showDartPad: showDartPad)); - return _interpolateSkeleton(type, snippetData, skeleton, metadata); - } -} diff --git a/dev/snippets/pubspec.yaml b/dev/snippets/pubspec.yaml deleted file mode 100644 index 5b2f176544a..00000000000 --- a/dev/snippets/pubspec.yaml +++ /dev/null @@ -1,99 +0,0 @@ -name: snippets -version: 0.1.0 -description: A code snippet dartdoc extension for Flutter API docs. -homepage: https://github.com/flutter/flutter - -environment: - sdk: ">=2.12.1 <3.0.0" - -dartdoc: - # Exclude this package from the hosted API docs (Ironically...). - nodoc: true - -dependencies: - args: 2.2.0 - dart_style: 2.0.3 - meta: 1.7.0 - platform: 3.0.0 - process: 4.2.3 - - _fe_analyzer_shared: 23.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 2.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - async: 2.8.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - charcode: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - cli_util: 0.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - collection: 1.15.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - convert: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - crypto: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - file: 6.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - glob: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - package_config: 2.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - pedantic: 1.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - pub_semver: 2.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - source_span: 1.8.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - string_scanner: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - term_glyph: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - typed_data: 1.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - watcher: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - yaml: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - -dev_dependencies: - test: 1.17.10 - - boolean_selector: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - coverage: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - http_multi_server: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - http_parser: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - js: 0.6.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - logging: 1.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - matcher: 0.12.10 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - pool: 1.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - shelf: 1.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - shelf_packages_handler: 3.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - shelf_static: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - shelf_web_socket: 1.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - source_map_stack_trace: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - source_maps: 0.10.10 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - stack_trace: 1.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - stream_channel: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - test_api: 0.4.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - test_core: 0.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - vm_service: 7.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - webkit_inspection_protocol: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - -executables: - snippets: null - - boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - http: 0.12.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - http_multi_server: 2.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - http_parser: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - io: 0.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - js: 0.6.1+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - json_rpc_2: 2.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - matcher: 0.12.3+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 0.9.6+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - multi_server_socket: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - node_preamble: 1.4.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - package_resolver: 1.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - pool: 1.3.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - pub_semver: 1.4.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - shelf: 0.7.3+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - shelf_packages_handler: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - shelf_static: 0.2.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - shelf_web_socket: 0.2.2+4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - source_map_stack_trace: 1.1.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - source_maps: 0.10.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - stack_trace: 1.9.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - stream_channel: 1.6.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - term_glyph: 1.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - vm_service_client: 0.2.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 1.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - -# PUBSPEC CHECKSUM: c51d diff --git a/dev/snippets/test/configuration_test.dart b/dev/snippets/test/configuration_test.dart deleted file mode 100644 index 26131ab655b..00000000000 --- a/dev/snippets/test/configuration_test.dart +++ /dev/null @@ -1,52 +0,0 @@ -// 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:io'; - -import 'package:snippets/configuration.dart'; -import 'package:test/test.dart' hide TypeMatcher, isInstanceOf; - -void main() { - group('Configuration', () { - late Configuration config; - - setUp(() { - config = Configuration(flutterRoot: Directory('/flutter sdk')); - }); - test('config directory is correct', () async { - expect(config.configDirectory.path, - matches(RegExp(r'[/\\]flutter sdk[/\\]dev[/\\]snippets[/\\]config'))); - }); - test('output directory is correct', () async { - expect(config.outputDirectory.path, - matches(RegExp(r'[/\\]flutter sdk[/\\]dev[/\\]docs[/\\]doc[/\\]snippets'))); - }); - test('skeleton directory is correct', () async { - expect(config.skeletonsDirectory.path, - matches(RegExp(r'[/\\]flutter sdk[/\\]dev[/\\]snippets[/\\]config[/\\]skeletons'))); - }); - test('templates directory is correct', () async { - expect(config.templatesDirectory.path, - matches(RegExp(r'[/\\]flutter sdk[/\\]dev[/\\]snippets[/\\]config[/\\]templates'))); - }); - test('html skeleton file for sample is correct', () async { - expect( - config.getHtmlSkeletonFile(SnippetType.snippet).path, - matches(RegExp( - r'[/\\]flutter sdk[/\\]dev[/\\]snippets[/\\]config[/\\]skeletons[/\\]snippet.html'))); - }); - test('html skeleton file for app with no dartpad is correct', () async { - expect( - config.getHtmlSkeletonFile(SnippetType.sample).path, - matches(RegExp( - r'[/\\]flutter sdk[/\\]dev[/\\]snippets[/\\]config[/\\]skeletons[/\\]sample.html'))); - }); - test('html skeleton file for app with dartpad is correct', () async { - expect( - config.getHtmlSkeletonFile(SnippetType.sample, showDartPad: true).path, - matches(RegExp( - r'[/\\]flutter sdk[/\\]dev[/\\]snippets[/\\]config[/\\]skeletons[/\\]dartpad-sample.html'))); - }); - }); -} diff --git a/dev/snippets/test/snippets_test.dart b/dev/snippets/test/snippets_test.dart deleted file mode 100644 index 7e54896d6e8..00000000000 --- a/dev/snippets/test/snippets_test.dart +++ /dev/null @@ -1,334 +0,0 @@ -// 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:convert'; -import 'dart:io' show Directory, File, Process, ProcessResult, ProcessSignal, ProcessStartMode, SystemEncoding; -import 'package:path/path.dart' as path; -import 'package:platform/platform.dart'; -import 'package:process/process.dart'; -import 'package:snippets/configuration.dart'; -import 'package:snippets/main.dart' show getChannelName; -import 'package:snippets/snippets.dart'; -import 'package:test/test.dart' hide TypeMatcher, isInstanceOf; - -void main() { - group('Generator', () { - late Configuration configuration; - late SnippetGenerator generator; - late Directory tmpDir; - late File template; - - setUp(() { - tmpDir = Directory.systemTemp.createTempSync('flutter_snippets_test.'); - configuration = Configuration(flutterRoot: Directory(path.join( - tmpDir.absolute.path, 'flutter'))); - configuration.createOutputDirectory(); - configuration.templatesDirectory.createSync(recursive: true); - configuration.skeletonsDirectory.createSync(recursive: true); - template = File(path.join(configuration.templatesDirectory.path, 'template.tmpl')); - template.writeAsStringSync(''' -// Flutter code sample for {{element}} - -{{description}} - -import 'package:flutter/material.dart'; -import '../foo.dart'; - -{{code-imports}} - -{{code-my-preamble}} - -main() { - {{code}} -} -'''); - configuration.getHtmlSkeletonFile(SnippetType.sample).writeAsStringSync(''' -
HTML Bits
-{{description}} -
{{code}}
-
{{app}}
-
More HTML Bits
-'''); - configuration.getHtmlSkeletonFile(SnippetType.snippet).writeAsStringSync(''' -
HTML Bits
-{{description}} -
{{code}}
-
More HTML Bits
-'''); - configuration.getHtmlSkeletonFile(SnippetType.sample, showDartPad: true).writeAsStringSync(''' -
HTML Bits (DartPad-style)
- -
More HTML Bits
-'''); - generator = SnippetGenerator(configuration: configuration); - }); - tearDown(() { - tmpDir.deleteSync(recursive: true); - }); - - test('generates samples', () async { - final File inputFile = File(path.join(tmpDir.absolute.path, 'snippet_in.txt')) - ..createSync(recursive: true) - ..writeAsStringSync(r''' -A description of the snippet. - -On several lines. - -```dart imports -import 'dart:ui'; -``` - -```my-dart_language my-preamble -const String name = 'snippet'; -``` - -```dart -void main() { - print('The actual $name.'); -} -``` -'''); - final File outputFile = File(path.join(tmpDir.absolute.path, 'snippet_out.txt')); - - final String html = generator.generate( - inputFile, - SnippetType.sample, - template: 'template', - metadata: { - 'id': 'id', - 'channel': 'stable', - 'element': 'MyElement', - }, - output: outputFile, - ); - expect(html, contains('
HTML Bits
')); - expect(html, contains('
More HTML Bits
')); - expect(html, contains(r'print('The actual $name.');')); - expect(html, contains('A description of the snippet.\n')); - expect(html, isNot(contains('sample_channel=stable'))); - expect( - html, - contains('// A description of the snippet.\n' - '//\n' - '// On several lines.\n')); - expect(html, contains('void main() {')); - - final String outputContents = outputFile.readAsStringSync(); - expect(outputContents, contains('// Flutter code sample for MyElement')); - expect(outputContents, contains('A description of the snippet.')); - expect(outputContents, contains('void main() {')); - expect(outputContents, contains("const String name = 'snippet';")); - final List lines = outputContents.split('\n'); - final int dartUiLine = lines.indexOf("import 'dart:ui';"); - final int materialLine = lines.indexOf("import 'package:flutter/material.dart';"); - final int otherLine = lines.indexOf("import '../foo.dart';"); - expect(dartUiLine, lessThan(materialLine)); - expect(materialLine, lessThan(otherLine)); - }); - - test('generates snippets', () async { - final File inputFile = File(path.join(tmpDir.absolute.path, 'snippet_in.txt')) - ..createSync(recursive: true) - ..writeAsStringSync(r''' -A description of the snippet. - -On several lines. - -```code -void main() { - print('The actual $name.'); -} -``` -'''); - - final String html = generator.generate( - inputFile, - SnippetType.snippet, - metadata: {'id': 'id'}, - ); - expect(html, contains('
HTML Bits
')); - expect(html, contains('
More HTML Bits
')); - expect(html, contains(r' print('The actual $name.');')); - expect(html, contains('
{@end-inject-html}A description of the snippet.\n\n' - 'On several lines.{@inject-html}
\n')); - expect(html, contains('main() {')); - }); - - test('generates dartpad samples', () async { - final File inputFile = File(path.join(tmpDir.absolute.path, 'snippet_in.txt')) - ..createSync(recursive: true) - ..writeAsStringSync(r''' -A description of the snippet. - -On several lines. - -```code -void main() { - print('The actual $name.'); -} -``` -'''); - - final String html = generator.generate( - inputFile, - SnippetType.sample, - showDartPad: true, - template: 'template', - metadata: {'id': 'id', 'channel': 'stable'}, - ); - expect(html, contains('
HTML Bits (DartPad-style)
')); - expect(html, contains('
More HTML Bits
')); - expect(html, contains('')); - }); - - test('generates sample metadata', () async { - final File inputFile = File(path.join(tmpDir.absolute.path, 'snippet_in.txt')) - ..createSync(recursive: true) - ..writeAsStringSync(r''' -A description of the snippet. - -On several lines. - -```code -void main() { - print('The actual $name.'); -} -``` -'''); - - final File outputFile = File(path.join(tmpDir.absolute.path, 'snippet_out.dart')); - final File expectedMetadataFile = File(path.join(tmpDir.absolute.path, 'snippet_out.json')); - - generator.generate( - inputFile, - SnippetType.sample, - template: 'template', - output: outputFile, - metadata: {'sourcePath': 'some/path.dart', 'id': 'id', 'channel': 'stable'}, - ); - expect(expectedMetadataFile.existsSync(), isTrue); - final Map json = jsonDecode(expectedMetadataFile.readAsStringSync()) as Map; - expect(json['id'], equals('id')); - expect(json['channel'], equals('stable')); - expect(json['file'], equals('snippet_out.dart')); - expect(json['description'], equals('A description of the snippet.\n\nOn several lines.')); - // Ensure any passed metadata is included in the output JSON too. - expect(json['sourcePath'], equals('some/path.dart')); - }); - }); - - group('getChannelName()', () { - test('does not call git if LUCI_BRANCH env var provided', () { - const String branch = 'stable'; - final FakePlatform platform = FakePlatform( - environment: {'LUCI_BRANCH': branch}, - ); - final FakeProcessManager processManager = FakeProcessManager([]); - expect( - getChannelName( - platform: platform, - processManager: processManager, - ), - branch, - ); - expect(processManager.hasRemainingExpectations, false); - }); - - test('calls git if LUCI_BRANCH env var is not provided', () { - const String branch = 'stable'; - final FakePlatform platform = FakePlatform( - environment: {}, - ); - final ProcessResult result = ProcessResult(0, 0, '## $branch...refs/heads/master', ''); - final FakeProcessManager processManager = FakeProcessManager( - [FakeCommand('git status -b --porcelain', result)], - ); - expect( - getChannelName( - platform: platform, - processManager: processManager, - ), - branch, - ); - expect(processManager.hasRemainingExpectations, false); - }); - }); -} - -const SystemEncoding systemEncoding = SystemEncoding(); - -class FakeCommand { - FakeCommand(this.command, [ProcessResult? result]) : _result = result; - final String command; - - final ProcessResult? _result; - ProcessResult get result => _result ?? ProcessResult(0, 0, '', ''); -} - -class FakeProcessManager implements ProcessManager { - FakeProcessManager(this.remainingExpectations); - - final List remainingExpectations; - - @override - bool canRun(dynamic command, {String? workingDirectory}) => true; - - @override - Future start( - List command, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false, - ProcessStartMode mode = ProcessStartMode.normal, - }) { - throw Exception('not implemented'); - } - - @override - Future run( - List command, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false, - Encoding stdoutEncoding = systemEncoding, - Encoding stderrEncoding = systemEncoding, - }) { - throw Exception('not implemented'); - } - - @override - ProcessResult runSync( - List command, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false, - Encoding stdoutEncoding = systemEncoding, - Encoding stderrEncoding = systemEncoding, - }) { - if (remainingExpectations.isEmpty) { - fail( - 'Called FakeProcessManager with $command when no further commands were expected!', - ); - } - final FakeCommand expectedCommand = remainingExpectations.removeAt(0); - final String expectedName = expectedCommand.command; - final String actualName = command.join(' '); - if (expectedName != actualName) { - fail( - 'FakeProcessManager expected the command $expectedName but received $actualName', - ); - } - return expectedCommand.result; - } - - bool get hasRemainingExpectations => remainingExpectations.isNotEmpty; - - @override - bool killPid(int pid, [ProcessSignal signal = ProcessSignal.sigterm]) { - throw Exception('not implemented'); - } -} diff --git a/dev/tools/dartdoc.dart b/dev/tools/dartdoc.dart index 2bd01565516..5cbcaa5eb74 100644 --- a/dev/tools/dartdoc.dart +++ b/dev/tools/dartdoc.dart @@ -118,14 +118,25 @@ Future main(List arguments) async { 'dartdoc', ]; - // Verify which version of dartdoc we're using. - final ProcessResult result = Process.runSync( + // Verify which version of snippets and dartdoc we're using. + final ProcessResult snippetsResult = Process.runSync( pubExecutable, - [...dartdocBaseArgs, '--version'], + [ + 'global', + 'list', + ], workingDirectory: kDocsRoot, environment: pubEnvironment, + stdoutEncoding: utf8, ); - print('\n${result.stdout}flutter version: $version\n'); + print(''); + final Iterable versionMatches = RegExp(r'^(?snippets|dartdoc) (?[^\s]+)', multiLine: true) + .allMatches(snippetsResult.stdout as String); + for (final RegExpMatch match in versionMatches) { + print('${match.namedGroup('name')} version: ${match.namedGroup('version')}'); + } + + print('flutter version: $version\n'); // Dartdoc warnings and errors in these packages are considered fatal. // All packages owned by flutter should be in the list. diff --git a/packages/flutter/lib/src/cupertino/page_scaffold.dart b/packages/flutter/lib/src/cupertino/page_scaffold.dart index 65b330868e5..5fa3e59b176 100644 --- a/packages/flutter/lib/src/cupertino/page_scaffold.dart +++ b/packages/flutter/lib/src/cupertino/page_scaffold.dart @@ -32,7 +32,7 @@ import 'theme.dart'; /// // Uncomment to change the background color /// // backgroundColor: CupertinoColors.systemPink, /// navigationBar: const CupertinoNavigationBar( -/// middle: const Text('Sample Code'), +/// middle: Text('Sample Code'), /// ), /// child: ListView( /// children: [ diff --git a/packages/flutter/lib/src/material/app_bar.dart b/packages/flutter/lib/src/material/app_bar.dart index 6f6c81e9979..88c61ada3c8 100644 --- a/packages/flutter/lib/src/material/app_bar.dart +++ b/packages/flutter/lib/src/material/app_bar.dart @@ -1410,7 +1410,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { /// child: SizedBox( /// height: 20, /// child: Center( -/// child: const Text('Scroll to see the SliverAppBar in effect.'), +/// child: Text('Scroll to see the SliverAppBar in effect.'), /// ), /// ), /// ), diff --git a/packages/flutter/lib/src/material/data_table.dart b/packages/flutter/lib/src/material/data_table.dart index 4389ef24b0c..0bc190f2c16 100644 --- a/packages/flutter/lib/src/material/data_table.dart +++ b/packages/flutter/lib/src/material/data_table.dart @@ -382,7 +382,7 @@ class DataCell { /// child: DataTable( /// columns: const [ /// DataColumn( -/// label: const Text('Number'), +/// label: Text('Number'), /// ), /// ], /// rows: List.generate( diff --git a/packages/flutter/lib/src/material/input_decorator.dart b/packages/flutter/lib/src/material/input_decorator.dart index 18ea3d756ec..a946c4c4867 100644 --- a/packages/flutter/lib/src/material/input_decorator.dart +++ b/packages/flutter/lib/src/material/input_decorator.dart @@ -2426,7 +2426,7 @@ class _InputDecoratorState extends State with TickerProviderStat /// hintText: 'Hint Text', /// helperText: 'Helper Text', /// counterText: '0 characters', -/// border: const OutlineInputBorder(), +/// border: OutlineInputBorder(), /// ), /// ); /// } diff --git a/packages/flutter/lib/src/material/progress_indicator.dart b/packages/flutter/lib/src/material/progress_indicator.dart index 89f056fab38..7f7f8dce5f6 100644 --- a/packages/flutter/lib/src/material/progress_indicator.dart +++ b/packages/flutter/lib/src/material/progress_indicator.dart @@ -281,7 +281,7 @@ class _LinearProgressIndicatorPainter extends CustomPainter { /// children: [ /// const Text( /// 'Linear progress indicator with a fixed color', -/// style: const TextStyle(fontSize: 20), +/// style: TextStyle(fontSize: 20), /// ), /// LinearProgressIndicator( /// value: controller.value, diff --git a/packages/flutter/lib/src/painting/gradient.dart b/packages/flutter/lib/src/painting/gradient.dart index d416ebf5a7d..03cdeb3e072 100644 --- a/packages/flutter/lib/src/painting/gradient.dart +++ b/packages/flutter/lib/src/painting/gradient.dart @@ -339,10 +339,10 @@ abstract class Gradient { /// Widget build(BuildContext context) { /// return Container( /// decoration: const BoxDecoration( -/// gradient: const LinearGradient( +/// gradient: LinearGradient( /// begin: Alignment.topLeft, /// end: Alignment(0.8, 0.0), // 10% of the width, so there are ten blinds. -/// colors: const [Color(0xffee0000), Color(0xffeeee00)], // red to yellow +/// colors: [Color(0xffee0000), Color(0xffeeee00)], // red to yellow /// tileMode: TileMode.repeated, // repeats the gradient over the canvas /// ), /// ), diff --git a/packages/flutter/lib/src/widgets/autocomplete.dart b/packages/flutter/lib/src/widgets/autocomplete.dart index 9684fcdb284..5eb804c71cf 100644 --- a/packages/flutter/lib/src/widgets/autocomplete.dart +++ b/packages/flutter/lib/src/widgets/autocomplete.dart @@ -444,7 +444,7 @@ typedef AutocompleteOptionToString = String Function(T option) /// ), /// ElevatedButton( /// onPressed: () { -/// FocusScope.of(context).requestFocus(new FocusNode()); +/// FocusScope.of(context).unfocus(); /// if (!_formKey.currentState!.validate()) { /// return; /// } diff --git a/packages/flutter/lib/src/widgets/focus_manager.dart b/packages/flutter/lib/src/widgets/focus_manager.dart index 9efd4b7f1bc..ea1715aff15 100644 --- a/packages/flutter/lib/src/widgets/focus_manager.dart +++ b/packages/flutter/lib/src/widgets/focus_manager.dart @@ -901,7 +901,7 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier { /// return const SizedBox( /// width: 200, /// child: Padding( - /// padding: const EdgeInsets.all(8.0), + /// padding: EdgeInsets.all(8.0), /// child: TextField( /// decoration: InputDecoration(border: OutlineInputBorder()), /// ), diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart index 30fd905ddd4..5309a36811e 100644 --- a/packages/flutter/lib/src/widgets/framework.dart +++ b/packages/flutter/lib/src/widgets/framework.dart @@ -4433,7 +4433,7 @@ typedef ErrorWidgetBuilder = Widget Function(FlutterErrorDetails details); /// alignment: Alignment.center, /// child: const Text( /// 'Error!', -/// style: const TextStyle(color: Colors.yellow), +/// style: TextStyle(color: Colors.yellow), /// textDirection: TextDirection.ltr, /// ), /// );