Skip to content

Commit 9ced09e

Browse files
gspencergoogitsjustkevin
authored andcommitted
Move snippets package back into flutter repo (flutter#147690)
This moves the snippets package back into the Flutter repo so that API documentation generation can happen without the use of `dart pub global run` because `pub run` doesn't handle concurrency well. The change modifies the dartdoc building process to include building an executable from the snippets tool and installing that in the cache directory for use during docs generation. The snippets tool will reside in dev/snippets, where it originally resided before being moved to https://github.com/flutter/assets-for-api-docs. The snippets code itself is unchanged from the code that is in https://github.com/flutter/assets-for-api-docs/packages/snippets. - flutter#144408 - flutter#147609 - flutter#147645 - Added snippets tests to the overall testing build.
1 parent 445718f commit 9ced09e

22 files changed

+4764
-18
lines changed

dartdoc_options.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ dartdoc:
55
# The dev/bots/docs.sh script does this automatically.
66
tools:
77
snippet:
8-
command: ["bin/cache/dart-sdk/bin/dart", "pub", "global", "run", "snippets", "--output-directory=doc/snippets", "--type=snippet"]
8+
command: ["bin/cache/artifacts/snippets/snippets", "--output-directory=doc/snippets", "--type=snippet"]
99
description: "Creates sample code documentation output from embedded documentation samples."
1010
sample:
11-
command: ["bin/cache/dart-sdk/bin/dart", "pub", "global", "run", "snippets", "--output-directory=doc/snippets", "--type=sample"]
11+
command: ["bin/cache/artifacts/snippets/snippets", "--output-directory=doc/snippets", "--type=sample"]
1212
description: "Creates full application sample code documentation output from embedded documentation samples."
1313
dartpad:
14-
command: ["bin/cache/dart-sdk/bin/dart", "pub", "global", "run", "snippets", "--output-directory=doc/snippets", "--type=dartpad"]
14+
command: ["bin/cache/artifacts/snippets/snippets", "--output-directory=doc/snippets", "--type=dartpad"]
1515
description: "Creates full application sample code documentation output from embedded documentation samples and displays it in an embedded DartPad."
1616
errors:
1717
## Default errors of dartdoc:

dev/bots/docs.sh

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,16 +107,25 @@ function parse_args() {
107107
fi
108108
}
109109

110+
function build_snippets_tool() (
111+
local snippets_dir="$FLUTTER_ROOT/dev/snippets"
112+
local output_dir="$FLUTTER_BIN/cache/artifacts/snippets"
113+
echo "Building snippets tool executable."
114+
command cd "$snippets_dir"
115+
mkdir -p "$output_dir"
116+
dart pub get
117+
dart compile exe -o "$output_dir/snippets" bin/snippets.dart
118+
)
119+
110120
function generate_docs() {
111121
# Install and activate dartdoc.
112122
# When updating to a new dartdoc version, please also update
113123
# `dartdoc_options.yaml` to include newly introduced error and warning types.
114124
"$DART" pub global activate dartdoc 8.0.6
115125

116-
# Install and activate the snippets tool, which resides in the
117-
# assets-for-api-docs repo:
118-
# https://github.com/flutter/assets-for-api-docs/tree/main/packages/snippets
119-
"$DART" pub global activate snippets 0.4.3
126+
# Build and install the snippets tool, which resides in
127+
# the dev/docs/snippets directory.
128+
build_snippets_tool
120129

121130
# This script generates a unified doc set, and creates
122131
# a custom index.html, placing everything into DOC_DIR.

dev/bots/test.dart

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -250,13 +250,14 @@ Future<void> main(List<String> args) async {
250250
'web_long_running_tests': () => webLongRunningTestsRunner(flutterRoot),
251251
'flutter_plugins': _runFlutterPackagesTests,
252252
'skp_generator': skpGeneratorTestsRunner,
253-
'realm_checker': _runRealmCheckerTest,
254-
'customer_testing': _runCustomerTesting,
255-
'analyze': _runAnalyze,
256-
'fuchsia_precache': _runFuchsiaPrecache,
257-
'docs': _runDocs,
258-
'verify_binaries_codesigned': _runVerifyCodesigned,
259-
kTestHarnessShardName: _runTestHarnessTests, // Used for testing this script; also run as part of SHARD=framework_tests, SUBSHARD=misc.
253+
'realm_checker': realmCheckerTestRunner,
254+
'customer_testing': customerTestingRunner,
255+
'analyze': analyzeRunner,
256+
'fuchsia_precache': fuchsiaPrecacheRunner,
257+
'snippets': _runSnippetsTests,
258+
'docs': docsRunner,
259+
'verify_binaries_codesigned': verifyCodesignedTestRunner,
260+
kTestHarnessShardName: testHarnessTestsRunner, // Used for testing this script; also run as part of SHARD=framework_tests, SUBSHARD=misc.
260261
});
261262
} catch (error, stackTrace) {
262263
foundError(<String>[
@@ -506,6 +507,21 @@ Future<void> _runToolTests() async {
506507
});
507508
}
508509

510+
Future<void> _runSnippetsTests() async {
511+
final String snippetsPath = path.join(flutterRoot, 'dev', 'snippets');
512+
final List<String> allTests = Directory(path.join(snippetsPath, 'test'))
513+
.listSync(recursive: true).whereType<File>()
514+
.map<String>((FileSystemEntity entry) => path.relative(entry.path, from: _toolsPath))
515+
.where((String testPath) => path.basename(testPath).endsWith('_test.dart')).toList();
516+
517+
await runDartTest(
518+
snippetsPath,
519+
forceSingleCore: true,
520+
testPaths: selectIndexOfTotalSubshard<String>(allTests),
521+
collectMetrics: true,
522+
);
523+
}
524+
509525
Future<void> runForbiddenFromReleaseTests() async {
510526
// Build a release APK to get the snapshot json.
511527
final Directory tempDirectory = Directory.systemTemp.createTempSync('flutter_forbidden_imports.');

dev/snippets/README.md

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
# Dartdoc Sample Generation
2+
3+
The Flutter API documentation contains code blocks that help provide context or
4+
a good starting point when learning to use any of Flutter's APIs.
5+
6+
To generate these code blocks, Flutter uses dartdoc tools to turn documentation
7+
in the source code into API documentation, as seen on [https://api.flutter.dev/]
8+
9+
## Table of Contents
10+
11+
- [Types of code blocks](#types-of-code-blocks)
12+
- [Snippet tool](#snippet-tool)
13+
- [Sample tool](#sample-tool)
14+
- [Skeletons](#skeletons)
15+
- [Test Doc Generation Workflow](#test-doc-generation-workflow)
16+
17+
## Types of code blocks
18+
19+
There are three kinds of code blocks.
20+
21+
- A `snippet`, which is a more or less context-free code snippet that we
22+
magically determine how to analyze.
23+
24+
- A `dartpad` sample, which gets placed into a full-fledged application, and can
25+
be executed inline in the documentation on the web page using
26+
DartPad.
27+
28+
- A `sample`, which gets placed into a full-fledged application, but isn't
29+
placed into DartPad in the documentation because it doesn't make sense to do
30+
so.
31+
32+
Ideally, every sample is a DartPad sample, but some samples don't have any visual
33+
representation and some just don't make sense that way (for example, sample
34+
code for setting the system UI's notification area color on Android won't do
35+
anything on the web).
36+
37+
### Snippet Tool
38+
39+
![Code snippet image](assets/code_snippet.png)
40+
41+
The code `snippet` tool generates a block containing a description and example
42+
code. Here is an example of the code `snippet` tool in use:
43+
44+
```dart
45+
/// {@tool snippet}
46+
///
47+
/// If the avatar is to have an image, the image should be specified in the
48+
/// [backgroundImage] property:
49+
///
50+
/// ```dart
51+
/// CircleAvatar(
52+
/// backgroundImage: NetworkImage(userAvatarUrl),
53+
/// )
54+
/// ```
55+
/// {@end-tool}
56+
```
57+
58+
This will generate sample code that can be copied to the clipboard and added to
59+
existing applications.
60+
61+
This uses the skeleton for `snippet` snippets when generating the HTML to put
62+
into the Dart docs. You can find this [template in the Flutter
63+
repo](https://github.com/flutter/flutter/blob/main/dev/snippets/config/skeletons/snippet.html).
64+
65+
#### Analysis
66+
67+
The
68+
[`analyze_sample_code.dart`](https://github.com/flutter/flutter/blob/main/dev/bots/analyze_sample_code.dart)
69+
script finds code inside the `@tool
70+
snippet` sections and uses the Dart analyzer to check them.
71+
72+
There are several kinds of sample code you can specify:
73+
74+
- Constructor calls, typically showing what might exist in a build method. These
75+
will be inserted into an assignment expression assigning to a variable of type
76+
"dynamic" and followed by a semicolon, for analysis.
77+
78+
- Class definitions. These start with "class", and are analyzed verbatim.
79+
80+
- Other code. It gets included verbatim, though any line that says `// ...` is
81+
considered to separate the block into multiple blocks to be processed
82+
individually.
83+
84+
The above means that it's tricky to include verbatim imperative code (e.g. a
85+
call to a method) since it won't be valid to have such code at the top level.
86+
Instead, wrap it in a function or even a whole class, or make it a valid
87+
variable declaration.
88+
89+
You can declare code that should be included in the analysis but not shown in
90+
the API docs by adding a comment "// Examples can assume:" to the file (usually
91+
at the top of the file, after the imports), following by one or more
92+
commented-out lines of code. That code is included verbatim in the analysis. For
93+
example:
94+
95+
```dart
96+
// Examples can assume:
97+
// final BuildContext context;
98+
// final String userAvatarUrl;
99+
```
100+
101+
You can assume that the entire Flutter framework and most common
102+
`dart:*` packages are imported and in scope; `dart:math` as `math` and
103+
`dart:ui` as `ui`.
104+
105+
### Sample Tool
106+
107+
![Code sample image](assets/code_sample.png)
108+
109+
The code `sample` and `dartpad` tools can expand sample code into full Flutter
110+
applications. These sample applications can be directly copied and used to
111+
demonstrate the API's functionality in a sample application, or used with the
112+
`flutter create` command to create a local project with the sample code. The
113+
`dartpad` samples are embedded into the API docs web page and are live
114+
applications in the API documentation.
115+
116+
```dart
117+
/// {@tool sample --template=stateless_widget_material}
118+
/// This example shows how to make a simple [FloatingActionButton] in a
119+
/// [Scaffold], with a pink [backgroundColor] and a thumbs up [Icon].
120+
///
121+
/// ```dart
122+
/// Widget build(BuildContext context) {
123+
/// return Scaffold(
124+
/// appBar: AppBar(
125+
/// title: Text('Floating Action Button Sample'),
126+
/// ),
127+
/// body: Center(
128+
/// child: Text('Press the button below!')
129+
/// ),
130+
/// floatingActionButton: FloatingActionButton(
131+
/// onPressed: () {
132+
/// // Add your onPressed code here!
133+
/// },
134+
/// child: Icon(Icons.thumb_up),
135+
/// backgroundColor: Colors.pink,
136+
/// ),
137+
/// );
138+
/// }
139+
/// ```
140+
/// {@end-tool}
141+
```
142+
143+
This uses the skeleton for [application](https://github.com/flutter/flutter/blob/main/dev/snippets/config/skeletons/sample.html)
144+
snippets in the Flutter repo.
145+
146+
The `sample` and `dartpad` tools also allow for quick Flutter app generation
147+
using the following command:
148+
149+
```bash
150+
flutter create --sample=[directory.File.sampleNumber] [name_of_project_directory]
151+
```
152+
153+
This command is displayed as part of the sample in the API docs.
154+
155+
#### Sample Analysis
156+
157+
The [`../bots/analyze_sample_code.dart`](../bots/analyze_sample_code.dart)
158+
script finds code inside the `@tool sample` sections and uses the Dart analyzer
159+
to check the sample code.
160+
161+
## Skeletons
162+
163+
A skeleton (concerning this tool) is an HTML template into which the Dart
164+
code blocks and descriptions are interpolated.
165+
166+
There is currently one skeleton for
167+
[application](https://github.com/flutter/flutter/blob/main/dev/snippets/config/skeletons/sample.html)
168+
samples, one for
169+
[dartpad](https://github.com/flutter/flutter/blob/main/dev/snippets/config/skeletons/dartpad-sample.html),
170+
and one for
171+
[snippet](https://github.com/flutter/flutter/blob/main/dev/snippets/config/skeletons/snippet.html)
172+
code samples, but there could be more.
173+
174+
Skeletons use mustache notation (e.g. `{{code}}`) to mark where components will
175+
be interpolated into the template. It doesn't use the mustache
176+
package since these are simple string substitutions, but it uses the same
177+
syntax.
178+
179+
The code block generation tools that process the source input and emit HTML for
180+
output, which dartdoc places back into the documentation. Any options given to
181+
the `{@tool ...}` directive are passed on verbatim to the tool.
182+
183+
The `snippets` tool renders these examples through a combination of markdown
184+
and HTML using the `{@inject-html}` dartdoc directive.
185+
186+
## Test Doc Generation Workflow
187+
188+
If you are making changes to an existing code block or are creating a new code
189+
block, follow these steps to generate a local copy of the API docs and verify
190+
that your code blocks are showing up correctly:
191+
192+
1. Make an update to a code block or create a new code block.
193+
2. From the root directory, run `./dev/bots/docs.sh`. This should start
194+
generating a local copy of the API documentation.
195+
Supplying the "--output" argument allows you to specify the output zip file
196+
for the completed documentation. Defaults to `api_docs.zip`` in the current
197+
directory.
198+
3. Once complete, unzip the files to the desired location and open the `index.html`
199+
within.
200+
201+
Note that generating the sample output will not allow you to run your code in
202+
DartPad, because DartPad pulls the code it runs from the appropriate docs server
203+
(main or stable).
204+
205+
Copy the generated code and paste it into a regular DartPad instance to test if
206+
it runs in DartPad. To get the code that will be produced by your documentation
207+
changes, run sample analysis locally (see the next section) and paste the output
208+
into a DartPad at [https://dartpad.dartlang.org].
209+
210+
## Running sample analysis locally
211+
212+
If all you want to do is analyze the sample code you have written locally, then
213+
generating the entire docs output takes a long time.
214+
215+
Instead, you can run the analysis locally with this command from the Flutter root:
216+
217+
```bash
218+
TMPDIR=/tmp bin/cache/dart-sdk/bin/dart dev/bots/analyze_sample_code.dart --temp=samples
219+
```
220+
221+
This will analyze the samples, and leave the generated files in `/tmp/samples`
222+
223+
You can find the sample you are working on in `/tmp/samples`. It is named using the
224+
path to the file it is in, and the line of the file that the `{@tool ...}` directive
225+
is on.
226+
227+
For example, the file `sample.src.widgets.animated_list.52.dart` points to the sample
228+
in `packages/flutter/src/widgets/animated_list.dart` at line 52. You can then take the
229+
contents of that file, and paste it into [Dartpad](https://dartpad.dev) and see if it
230+
works. If the sample relies on new features that have just landed, it may not work
231+
until the features make it into the `dev` branch.

0 commit comments

Comments
 (0)