Dartdoc snippet extension to inject full featured code snippets in to API docs. (#23281)
This creates a custom dartdoc tool that will generate snippet blocks in our API docs that allow the user to copy easily to the clipboard, and will also embed the snippet code into a template to show it in a larger context with an app.
This PR adds the snippet tool, a template, and a couple of HTML skeleton files, one for snippets that are designed to be in an application setting, and one where it simply puts a nice container around existing snippets, making them easier to copy to the clipboard.
diff --git a/dev/snippets/config/skeletons/application.html b/dev/snippets/config/skeletons/application.html
new file mode 100644
index 0000000..bbbed4f
--- /dev/null
+++ b/dev/snippets/config/skeletons/application.html
@@ -0,0 +1,34 @@
+{@inject-html}
+<div class="snippet-buttons">
+ <button id="shortSnippetButton" onclick="showSnippet(shortSnippet);" selected>Sample</button>
+ <button id="longSnippetButton" onclick="showSnippet(longSnippet);">Sample in an App</button>
+</div>
+<div class="snippet-container">
+ <div class="snippet" id="shortSnippet">
+ <div class="snippet-description">
+ {@end-inject-html}
+ {{description}}
+ {@inject-html}
+ </div>
+ <div class="copyable-container">
+ <button class="copy-button-overlay copy-button" title="Copy to clipboard"
+ onclick="copyTextToClipboard();">
+ <i class="material-icons copy-image">assignment</i>
+ </button>
+ <pre class="language-dart"><code class="language-dart">{{code}}</code></pre>
+ </div>
+ </div>
+ <div class="snippet" id="longSnippet" hidden>
+ <div class="snippet-description">To create a sample project with this code snippet, run:<br/>
+ <span class="snippet-create-command">flutter create --snippet={{id}} mysample</span>
+ </div>
+ <div class="copyable-container">
+ <button class="copy-button-overlay copy-button" title="Copy to clipboard"
+ onclick="copyTextToClipboard();">
+ <i class="material-icons copy-image">assignment</i>
+ </button>
+ <pre class="language-dart"><code class="language-dart">{{app}}</code></pre>
+ </div>
+ </div>
+</div>
+{@end-inject-html}
diff --git a/dev/snippets/config/skeletons/sample.html b/dev/snippets/config/skeletons/sample.html
new file mode 100644
index 0000000..9343a01
--- /dev/null
+++ b/dev/snippets/config/skeletons/sample.html
@@ -0,0 +1,20 @@
+{@inject-html}
+<div class="snippet-container">
+ <div class="snippet">
+ <div class="snippet-description">
+ {@end-inject-html}
+ {{description}}
+ {@inject-html}
+ </div>
+ <div class="copyable-container">
+ <button class="copy-button-overlay copy-button" title="Copy to clipboard"
+ onclick="copyTextToClipboard(findSiblingWithId(this, 'sample-code'));">
+ <i class="material-icons copy-image">assignment</i>
+ </button>
+ <pre class="language-dart" id="sample-code">
+ <code class="language-dart">{{code}}</code>
+ </pre>
+ </div>
+ </div>
+</div>
+{@end-inject-html}
diff --git a/dev/snippets/config/templates/README.md b/dev/snippets/config/templates/README.md
new file mode 100644
index 0000000..e5addd0
--- /dev/null
+++ b/dev/snippets/config/templates/README.md
@@ -0,0 +1,56 @@
+## Creating Code Snippets
+
+In general, creating application snippets can be accomplished with the following
+syntax inside of the dartdoc comment for a Flutter class/variable/enum/etc.:
+
+```dart
+/// {@tool snippet --template=stateful_widget}
+/// Any text outside of the code blocks will be accumulated and placed at the
+/// top of the snippet box as a description. Don't try and say "see the code
+/// above" or "see the code below", since the location of the description may
+/// change in the future. You can use dartdoc [Linking] in the description, and
+/// __Markdown__ too.
+/// ```dart preamble
+/// class Foo extends StatelessWidget {
+/// const Foo({this.value = ''});
+///
+/// String value;
+///
+/// @override
+/// Widget build(BuildContext context) {
+/// return Text(value);
+/// }
+/// }
+/// ```
+/// This will get tacked on to the end of the description above, and shown above
+/// the snippet. These two code blocks will be separated by `///...` in the
+/// short version of the snippet code sample.
+/// ```dart
+/// String myValue = 'Foo';
+///
+/// @override
+/// Widget build(BuildContext) {
+/// return const Foo(myValue);
+/// }
+/// ```
+/// {@end-tool}
+```
+
+This will result in the template having the section that's inside "```dart"
+interpolated into the template's stateful widget's state object body.
+
+All code within a code block in a snippet needs to be able to be run through
+dartfmt without errors, so it needs to be valid code (This shouldn't be an
+additional burden, since all code will also be compiled to be sure it compiles).
+
+## Available Templates
+
+The templates available for using as an argument to the snippets tool are as
+follows:
+
+- __`stateful_widget`__ : Takes a `preamble` in addition to the default code
+ block, which will be placed at the top level of the Dart file, so bare
+ function calls are not allowed in the preamble. The default code block is
+ placed as the body of a stateful widget, so you will need to implement the
+ build() function, and any state variables.
+
diff --git a/dev/snippets/config/templates/stateful_widget.tmpl b/dev/snippets/config/templates/stateful_widget.tmpl
new file mode 100644
index 0000000..aff8fbf
--- /dev/null
+++ b/dev/snippets/config/templates/stateful_widget.tmpl
@@ -0,0 +1,32 @@
+{{description}}
+
+import 'package:flutter/material.dart';
+
+void main() => runApp(new MyApp());
+
+class MyApp extends StatelessWidget {
+ // This widget is the root of your application.
+ @override
+ Widget build(BuildContext context) {
+ return new MaterialApp(
+ title: 'Flutter Code Sample for {{id}}',
+ theme: new ThemeData(
+ primarySwatch: Colors.blue,
+ ),
+ home: new MyHomePage(title: '{{id}} Sample'),
+ );
+ }
+}
+
+{{code-preamble}}
+
+class MyHomePage extends StatelessWidget {
+ MyHomePage({Key key}) : super(key: key);
+
+ @override
+ _MyHomePageState createState() => new _MyHomePageState();
+}
+
+class _MyHomePageState extends State<MyHomePage> {
+ {{code}}
+}