Adds DartPad option to the DartDoc snippet generator. (#39924)
This commit is contained in:
parent
440a911034
commit
691977755c
@ -9,3 +9,6 @@ dartdoc:
|
|||||||
sample:
|
sample:
|
||||||
command: ["dev/snippets/lib/main.dart", "--type=sample"]
|
command: ["dev/snippets/lib/main.dart", "--type=sample"]
|
||||||
description: "Creates sample code documentation output from embedded documentation samples."
|
description: "Creates sample code documentation output from embedded documentation samples."
|
||||||
|
dartpad:
|
||||||
|
command: ["dev/snippets/lib/main.dart", "--type=application", "--dartpad"]
|
||||||
|
description: "Creates sample code documentation output from embedded documentation samples and displays it in an embedded DartPad."
|
||||||
|
@ -83,6 +83,11 @@
|
|||||||
font-family: courier, lucidia;
|
font-family: courier, lucidia;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.snippet-dartpad {
|
||||||
|
width: 100%;
|
||||||
|
height: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
.anchor-container {
|
.anchor-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
38
dev/snippets/config/skeletons/dartpad-application.html
Normal file
38
dev/snippets/config/skeletons/dartpad-application.html
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{@inject-html}
|
||||||
|
<a name="{{id}}"></a>
|
||||||
|
<div class="anchor-container">
|
||||||
|
<a class="anchor-button-overlay anchor-button" title="Copy link to clipboard"
|
||||||
|
onmouseenter="fixHref(this, '{{id}}');"
|
||||||
|
onclick="fixHref(this, '{{id}}'); copyStringToClipboard(this.href);"
|
||||||
|
href="#">
|
||||||
|
<i class="material-icons copy-image">link</i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="snippet-buttons">
|
||||||
|
<script>var visibleSnippet{{serial}} = "longSnippet{{serial}}";</script>
|
||||||
|
<button id="longSnippet{{serial}}Button"
|
||||||
|
onclick="visibleSnippet{{serial}} = showSnippet('longSnippet{{serial}}', visibleSnippet{{serial}});"
|
||||||
|
selected>
|
||||||
|
Interactive App
|
||||||
|
</button>
|
||||||
|
<button id="shortSnippet{{serial}}Button"
|
||||||
|
onclick="visibleSnippet{{serial}} = showSnippet('shortSnippet{{serial}}', visibleSnippet{{serial}});">
|
||||||
|
Sample code
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="snippet-container">
|
||||||
|
<div class="snippet" id="longSnippet{{serial}}">
|
||||||
|
<iframe class="snippet-dartpad" src="https://dartpad.dev/embed-flutter.html?split=60&run=true&sample_id={{id}}"></iframe>
|
||||||
|
</div>
|
||||||
|
<div class="snippet" id="shortSnippet{{serial}}" hidden>
|
||||||
|
{{description}}
|
||||||
|
<div class="copyable-container">
|
||||||
|
<button class="copy-button-overlay copy-button" title="Copy to clipboard"
|
||||||
|
onclick="copyTextToClipboard(visibleSnippet{{serial}});">
|
||||||
|
<i class="material-icons copy-image">assignment</i>
|
||||||
|
</button>
|
||||||
|
<pre class="language-{{language}}"><code class="language-{{language}}">{{code}}</code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{@end-inject-html}
|
@ -67,8 +67,12 @@ class Configuration {
|
|||||||
/// dartdoc.
|
/// dartdoc.
|
||||||
Directory get templatesDirectory => Directory(path.join(configDirectory.path, 'templates'));
|
Directory get templatesDirectory => Directory(path.join(configDirectory.path, 'templates'));
|
||||||
|
|
||||||
/// Gets the skeleton file to use for the given [SnippetType].
|
/// Gets the skeleton file to use for the given [SnippetType] and DartPad preference.
|
||||||
File getHtmlSkeletonFile(SnippetType type) {
|
File getHtmlSkeletonFile(SnippetType type, {bool showDartPad = false}) {
|
||||||
return File(path.join(skeletonsDirectory.path, '${getEnumName(type)}.html'));
|
assert(!showDartPad || type == SnippetType.application,
|
||||||
|
'Only application snippets work with dartpad.');
|
||||||
|
final String filename =
|
||||||
|
'${showDartPad ? 'dartpad-' : ''}${getEnumName(type)}.html';
|
||||||
|
return File(path.join(skeletonsDirectory.path, filename));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ const String _kOutputOption = 'output';
|
|||||||
const String _kPackageOption = 'package';
|
const String _kPackageOption = 'package';
|
||||||
const String _kTemplateOption = 'template';
|
const String _kTemplateOption = 'template';
|
||||||
const String _kTypeOption = 'type';
|
const String _kTypeOption = 'type';
|
||||||
|
const String _kShowDartPad = 'dartpad';
|
||||||
|
|
||||||
/// Generates snippet dartdoc output for a given input, and creates any sample
|
/// Generates snippet dartdoc output for a given input, and creates any sample
|
||||||
/// applications needed by the snippet.
|
/// applications needed by the snippet.
|
||||||
@ -87,6 +88,14 @@ void main(List<String> argList) {
|
|||||||
negatable: false,
|
negatable: false,
|
||||||
help: 'Prints help documentation for this command',
|
help: 'Prints help documentation for this command',
|
||||||
);
|
);
|
||||||
|
parser.addFlag(
|
||||||
|
_kShowDartPad,
|
||||||
|
defaultsTo: false,
|
||||||
|
negatable: false,
|
||||||
|
help: 'Indicates whether DartPad should be included in the snippet\'s '
|
||||||
|
'final HTML output. This flag only applies when the type parameter is '
|
||||||
|
'"application".',
|
||||||
|
);
|
||||||
|
|
||||||
final ArgResults args = parser.parse(argList);
|
final ArgResults args = parser.parse(argList);
|
||||||
|
|
||||||
@ -99,6 +108,11 @@ void main(List<String> argList) {
|
|||||||
.firstWhere((SnippetType type) => getEnumName(type) == args[_kTypeOption], orElse: () => null);
|
.firstWhere((SnippetType type) => getEnumName(type) == args[_kTypeOption], orElse: () => null);
|
||||||
assert(snippetType != null, "Unable to find '${args[_kTypeOption]}' in SnippetType enum.");
|
assert(snippetType != null, "Unable to find '${args[_kTypeOption]}' in SnippetType enum.");
|
||||||
|
|
||||||
|
if (args[_kShowDartPad] == true && snippetType != SnippetType.application) {
|
||||||
|
errorExit('${args[_kTypeOption]} was selected, but the --dartpad flag is only valid '
|
||||||
|
'for application snippets.');
|
||||||
|
}
|
||||||
|
|
||||||
if (args[_kInputOption] == null) {
|
if (args[_kInputOption] == null) {
|
||||||
stderr.writeln(parser.usage);
|
stderr.writeln(parser.usage);
|
||||||
errorExit('The --$_kInputOption option must be specified, either on the command '
|
errorExit('The --$_kInputOption option must be specified, either on the command '
|
||||||
@ -151,6 +165,7 @@ void main(List<String> argList) {
|
|||||||
stdout.write(generator.generate(
|
stdout.write(generator.generate(
|
||||||
input,
|
input,
|
||||||
snippetType,
|
snippetType,
|
||||||
|
showDartPad: args[_kShowDartPad],
|
||||||
template: template,
|
template: template,
|
||||||
output: args[_kOutputOption] != null ? File(args[_kOutputOption]) : null,
|
output: args[_kOutputOption] != null ? File(args[_kOutputOption]) : null,
|
||||||
metadata: <String, Object>{
|
metadata: <String, Object>{
|
||||||
|
@ -202,6 +202,11 @@ class SnippetGenerator {
|
|||||||
/// The [type] is the type of snippet to create: either a
|
/// The [type] is the type of snippet to create: either a
|
||||||
/// [SnippetType.application] or a [SnippetType.sample].
|
/// [SnippetType.application] or a [SnippetType.sample].
|
||||||
///
|
///
|
||||||
|
/// [showDartPad] indicates whether DartPad should be shown where possible.
|
||||||
|
/// Currently, this value only has an effect if [type] is
|
||||||
|
/// [SnippetType.application], 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
|
/// The [template] must not be null if the [type] is
|
||||||
/// [SnippetType.application], and specifies the name of the template to use
|
/// [SnippetType.application], and specifies the name of the template to use
|
||||||
/// for the application code.
|
/// for the application code.
|
||||||
@ -212,6 +217,7 @@ class SnippetGenerator {
|
|||||||
String generate(
|
String generate(
|
||||||
File input,
|
File input,
|
||||||
SnippetType type, {
|
SnippetType type, {
|
||||||
|
bool showDartPad = false,
|
||||||
String template,
|
String template,
|
||||||
File output,
|
File output,
|
||||||
@required Map<String, Object> metadata,
|
@required Map<String, Object> metadata,
|
||||||
@ -219,6 +225,8 @@ class SnippetGenerator {
|
|||||||
assert(template != null || type != SnippetType.application);
|
assert(template != null || type != SnippetType.application);
|
||||||
assert(metadata != null && metadata['id'] != null);
|
assert(metadata != null && metadata['id'] != null);
|
||||||
assert(input != null);
|
assert(input != null);
|
||||||
|
assert(!showDartPad || type == SnippetType.application,
|
||||||
|
'Only application snippets work with dartpad.');
|
||||||
final List<_ComponentTuple> snippetData = parseInput(_loadFileAsUtf8(input));
|
final List<_ComponentTuple> snippetData = parseInput(_loadFileAsUtf8(input));
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SnippetType.application:
|
case SnippetType.application:
|
||||||
@ -266,7 +274,8 @@ class SnippetGenerator {
|
|||||||
case SnippetType.sample:
|
case SnippetType.sample:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
final String skeleton = _loadFileAsUtf8(configuration.getHtmlSkeletonFile(type));
|
final String skeleton =
|
||||||
|
_loadFileAsUtf8(configuration.getHtmlSkeletonFile(type, showDartPad: showDartPad));
|
||||||
return interpolateSkeleton(type, snippetData, skeleton, metadata);
|
return interpolateSkeleton(type, snippetData, skeleton, metadata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,11 +31,23 @@ void main() {
|
|||||||
expect(config.templatesDirectory.path,
|
expect(config.templatesDirectory.path,
|
||||||
matches(RegExp(r'[/\\]flutter sdk[/\\]dev[/\\]snippets[/\\]config[/\\]templates')));
|
matches(RegExp(r'[/\\]flutter sdk[/\\]dev[/\\]snippets[/\\]config[/\\]templates')));
|
||||||
});
|
});
|
||||||
test('html skeleton file is correct', () async {
|
test('html skeleton file for sample 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 no dartpad is correct', () async {
|
||||||
expect(
|
expect(
|
||||||
config.getHtmlSkeletonFile(SnippetType.application).path,
|
config.getHtmlSkeletonFile(SnippetType.application).path,
|
||||||
matches(RegExp(
|
matches(RegExp(
|
||||||
r'[/\\]flutter sdk[/\\]dev[/\\]snippets[/\\]config[/\\]skeletons[/\\]application.html')));
|
r'[/\\]flutter sdk[/\\]dev[/\\]snippets[/\\]config[/\\]skeletons[/\\]application.html')));
|
||||||
});
|
});
|
||||||
|
test('html skeleton file for app with dartpad is correct', () async {
|
||||||
|
expect(
|
||||||
|
config.getHtmlSkeletonFile(SnippetType.application, showDartPad: true).path,
|
||||||
|
matches(RegExp(
|
||||||
|
r'[/\\]flutter sdk[/\\]dev[/\\]snippets[/\\]config[/\\]skeletons[/\\]dartpad-application.html')));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,11 @@ main() {
|
|||||||
{{description}}
|
{{description}}
|
||||||
<pre>{{code}}</pre>
|
<pre>{{code}}</pre>
|
||||||
<div>More HTML Bits</div>
|
<div>More HTML Bits</div>
|
||||||
|
''');
|
||||||
|
configuration.getHtmlSkeletonFile(SnippetType.application, showDartPad: true).writeAsStringSync('''
|
||||||
|
<div>HTML Bits (DartPad-style)</div>
|
||||||
|
<iframe class="snippet-dartpad" src="https://dartpad.dev/embed-flutter.html?split=60&run=true&sample_id={{id}}"></iframe>
|
||||||
|
<div>More HTML Bits</div>
|
||||||
''');
|
''');
|
||||||
generator = SnippetGenerator(configuration: configuration);
|
generator = SnippetGenerator(configuration: configuration);
|
||||||
});
|
});
|
||||||
@ -109,7 +114,11 @@ void main() {
|
|||||||
```
|
```
|
||||||
''');
|
''');
|
||||||
|
|
||||||
final String html = generator.generate(inputFile, SnippetType.sample, metadata: <String, Object>{'id': 'id'});
|
final String html = generator.generate(
|
||||||
|
inputFile,
|
||||||
|
SnippetType.sample,
|
||||||
|
metadata: <String, Object>{'id': 'id'},
|
||||||
|
);
|
||||||
expect(html, contains('<div>HTML Bits</div>'));
|
expect(html, contains('<div>HTML Bits</div>'));
|
||||||
expect(html, contains('<div>More HTML Bits</div>'));
|
expect(html, contains('<div>More HTML Bits</div>'));
|
||||||
expect(html, contains(' print('The actual \$name.');'));
|
expect(html, contains(' print('The actual \$name.');'));
|
||||||
@ -119,6 +128,33 @@ void main() {
|
|||||||
expect(html, contains('main() {'));
|
expect(html, contains('main() {'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('generates dartpad snippets', () async {
|
||||||
|
final File inputFile = File(path.join(tmpDir.absolute.path, 'snippet_in.txt'))
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync('''
|
||||||
|
A description of the snippet.
|
||||||
|
|
||||||
|
On several lines.
|
||||||
|
|
||||||
|
```code
|
||||||
|
void main() {
|
||||||
|
print('The actual \$name.');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
''');
|
||||||
|
|
||||||
|
final String html = generator.generate(
|
||||||
|
inputFile,
|
||||||
|
SnippetType.application,
|
||||||
|
showDartPad: true,
|
||||||
|
template: 'template',
|
||||||
|
metadata: <String, Object>{'id': 'id'},
|
||||||
|
);
|
||||||
|
expect(html, contains('<div>HTML Bits (DartPad-style)</div>'));
|
||||||
|
expect(html, contains('<div>More HTML Bits</div>'));
|
||||||
|
expect(html, contains('<iframe class="snippet-dartpad" src="https://dartpad.dev/embed-flutter.html?split=60&run=true&sample_id=id"></iframe>'));
|
||||||
|
});
|
||||||
|
|
||||||
test('generates snippet application metadata', () async {
|
test('generates snippet application metadata', () async {
|
||||||
final File inputFile = File(path.join(tmpDir.absolute.path, 'snippet_in.txt'))
|
final File inputFile = File(path.join(tmpDir.absolute.path, 'snippet_in.txt'))
|
||||||
..createSync(recursive: true)
|
..createSync(recursive: true)
|
||||||
|
@ -89,7 +89,7 @@ class _ToolbarContainerLayout extends SingleChildLayoutDelegate {
|
|||||||
/// to false. In that case a null leading widget will result in the middle/title widget
|
/// to false. In that case a null leading widget will result in the middle/title widget
|
||||||
/// stretching to start.
|
/// stretching to start.
|
||||||
///
|
///
|
||||||
/// {@tool snippet --template=stateless_widget_material}
|
/// {@tool dartpad --template=stateless_widget_material}
|
||||||
///
|
///
|
||||||
/// This sample shows an [AppBar] with two simple actions. The first action
|
/// This sample shows an [AppBar] with two simple actions. The first action
|
||||||
/// opens a [SnackBar], while the second action navigates to a new page.
|
/// opens a [SnackBar], while the second action navigates to a new page.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user