From e0ff483488d8790a64abf9ff497f209e23bf335a Mon Sep 17 00:00:00 2001 From: Parker Lougheed Date: Thu, 30 May 2024 16:03:47 -0500 Subject: [PATCH] Support specifying attributes to DartPad (#5884) Enables the mechanism for adding a `title` attribute to DartPad code blocks. ````markdown ```dartpad run="true" title="The classic hello world program in Dart!" theme="dark" void main() { print('Hello world!'); } ``` ```` Contributes to https://github.com/dart-lang/site-www/issues/5251 --- src/_11ty/plugins/highlight.js | 11 ++++--- src/content/codelabs/async-await.md | 16 ++++----- src/content/codelabs/dart-cheatsheet.md | 34 ++++++++++---------- src/content/codelabs/iterables.md | 24 +++++++------- src/content/language/callable-objects.md | 2 +- src/content/language/constructors.md | 4 +-- src/content/language/functions.md | 2 +- src/content/libraries/async/using-streams.md | 4 +-- src/content/overview.md | 2 +- src/content/tutorials/server/fetch-data.md | 2 +- src/content/tutorials/server/get-started.md | 2 +- 11 files changed, 53 insertions(+), 50 deletions(-) diff --git a/src/_11ty/plugins/highlight.js b/src/_11ty/plugins/highlight.js index 68444a49cc..ffe88c3b15 100644 --- a/src/_11ty/plugins/highlight.js +++ b/src/_11ty/plugins/highlight.js @@ -86,13 +86,16 @@ function _highlight( language, attributeString, ) { + const attributes = _parseAttributes(attributeString); + // Specially handle DartPad snippets so that inject_embed can convert them. - if (language.includes('-dartpad')) { - return `
${markdown.utils.escapeHtml(content)}
`; + if (language.includes('dartpad')) { + const theme = attributes['theme'] ?? 'light'; + const title = attributes['title'] ?? 'Runnable Dart sample'; + const runAutomatically = attributes['run'] ?? 'false'; + return `
${markdown.utils.escapeHtml(content)}
`; } - const attributes = _parseAttributes(attributeString); - const showLineNumbers = 'showLineNumbers' in attributes; let startingLineNumber = 0; if (showLineNumbers) { diff --git a/src/content/codelabs/async-await.md b/src/content/codelabs/async-await.md index fda09b3198..4e101ecf30 100644 --- a/src/content/codelabs/async-await.md +++ b/src/content/codelabs/async-await.md @@ -70,7 +70,7 @@ Before running this example, try to spot the issue -- what do you think the output will be? -```dart:run-dartpad:height-380px:ga_id-incorrect_usage +```dartpad // This example shows how *not* to write asynchronous Dart code. String createOrderMessage() { @@ -170,7 +170,7 @@ try to predict which will print first: "Large Latte" or "Fetching user order...". -```dart:run-dartpad:height-300px:ga_id-introducting_futures +```dartpad Future fetchUserOrder() { // Imagine that this function is fetching user info from another service or database. return Future.delayed(const Duration(seconds: 2), () => print('Large Latte')); @@ -194,7 +194,7 @@ Run the following example to see how a future completes with an error. A bit later you'll learn how to handle the error. -```dart:run-dartpad:height-300px:ga_id-completing_with_error +```dartpad Future fetchUserOrder() { // Imagine that this function is fetching user info but encounters a bug. return Future.delayed( @@ -375,7 +375,7 @@ within an `async` function body. What do you think the output will be? -```dart:run-dartpad:height-530px:ga_id-execution_within_async_function +```dartpad Future printOrderMessage() async { print('Awaiting user order...'); var order = await fetchUserOrder(); @@ -457,7 +457,7 @@ Implement an `async` function `reportLogins()` so that it does the following: * Example return value from `reportLogins()`: `"Total number of logins: 57"` * Gets the number of logins by calling the provided function `fetchLoginAmount()`. -```dart:run-dartpad:theme-dark:height-380px:ga_id-practice_using +```dartpad theme="dark" // Part 1 // Call the provided async function fetchRole() // to return the user role. @@ -640,7 +640,7 @@ from an asynchronous function. What do you think the output will be? -```dart:run-dartpad:height-530px:ga_id-try_catch +```dartpad Future printOrderMessage() async { try { print('Awaiting user order...'); @@ -693,7 +693,7 @@ that does the following: and [Errors.]({{site.dart-api}}/{{site.sdkInfo.channel}}/dart-core/Error-class.html) -```dart:run-dartpad:theme-dark:height-380px:ga_id-practice_errors +```dartpad theme="dark" // TODO: Implement changeUsername here. changeUsername() {} @@ -861,7 +861,7 @@ Write the following: `' Thanks, see you next time'`, where `` is the string value returned by calling `logoutUser()`. -```dart:run-dartpad:theme-dark:height-380px:ga_id-putting_it_all_together +```dartpad theme="dark" // Part 1 addHello(String user) {} diff --git a/src/content/codelabs/dart-cheatsheet.md b/src/content/codelabs/dart-cheatsheet.md index e762ddd814..99b83bfd4b 100644 --- a/src/content/codelabs/dart-cheatsheet.md +++ b/src/content/codelabs/dart-cheatsheet.md @@ -48,7 +48,7 @@ The following function takes two integers as parameters. Make it return a string containing both integers separated by a space. For example, `stringify(2, 3)` should return `'2 3'`. -```dart:run-dartpad:ga_id-string_interpolation +```dartpad String stringify(int x, int y) { TODO('Return a formatted string here'); } @@ -123,7 +123,7 @@ Try to declare two variables below: Ignore all initial errors in the DartPad. -```dart:run-dartpad:ga_id-nullable_variables +```dartpad // TODO: Declare the two variables here @@ -192,7 +192,7 @@ to implement the described behavior in the following snippet. Ignore all initial errors in the DartPad. -```dart:run-dartpad:height-255px:ga_id-null_aware +```dartpad String? foo = 'a string'; String? bar; // = null @@ -282,7 +282,7 @@ The following function takes a nullable string as a parameter. Try using conditional property access to make it return the uppercase version of `str`, or `null` if `str` is `null`. -```dart:run-dartpad:ga_id-conditional-property_access +```dartpad String? upperCaseIt(String? str) { // TODO: Try conditionally accessing the `toUpperCase` method here. } @@ -372,7 +372,7 @@ final aListOfBaseType = [SubType(), SubType()]; Try setting the following variables to the indicated values. Replace the existing null values. -```dart:run-dartpad:height-400px:ga_id-collection_literals +```dartpad // Assign this a list containing 'a', 'b', and 'c' in that order: final aListOfStrings = null; @@ -502,7 +502,7 @@ bool hasEmpty = aListOfStrings.any((s) => s.isEmpty); Try finishing the following statements, which use arrow syntax. -```dart:run-dartpad:height-345px:ga_id-arrow_syntax +```dartpad class MyClass { int value1 = 2; int value2 = 3; @@ -654,7 +654,7 @@ sets the `anInt`, `aString`, and `aList` properties of a `BigObject` to `1`, `'String!'`, and `[3.0]` (respectively) and then calls `allDone()`. -```dart:run-dartpad:height-345px:ga_id-cascades +```dartpad class BigObject { int anInt = 0; String aString = ''; @@ -791,7 +791,7 @@ Add the following: Ignore all initial errors in the DartPad. -```dart:run-dartpad:height-240px:ga_id-getters_setters +```dartpad class InvalidPriceException {} class ShoppingCart { @@ -948,7 +948,7 @@ Here are some examples of function calls and returned values:
-```dart:run-dartpad:ga_id-optional_positional_parameters +```dartpad String joinWithCommas(int a, [int? b, int? c, int? d, int? e]) { return TODO(); } @@ -1090,7 +1090,7 @@ then copy its value into `anInt`. Ignore all initial errors in the DartPad. -```dart:run-dartpad:height-310px:ga_id-optional_named_parameters +```dartpad class MyDataObject { final int anInt; final String aString; @@ -1258,7 +1258,7 @@ then do the following: * After everything's caught and handled, call `logger.doneLogging` (try using `finally`). -```dart:run-dartpad:height-420px:ga_id-exceptions +```dartpad typedef VoidFunction = void Function(); class ExceptionWithMessage { @@ -1453,7 +1453,7 @@ all three properties of the class. Ignore all initial errors in the DartPad. -```dart:run-dartpad:ga_id-this_constructor +```dartpad class MyClass { final int anInt; final String aString; @@ -1568,7 +1568,7 @@ FINALLY: Suggest using https://pub.dev/packages/characters if this is a user-entered string. {% endcomment %} -```dart:run-dartpad:ga_id-initializer_lists +```dartpad class FirstTwoLetters { final String letterOne; final String letterTwo; @@ -1670,7 +1670,7 @@ that sets all three properties to zero. Ignore all initial errors in the DartPad. -```dart:run-dartpad:height-240px:ga_id-named_constructors +```dartpad class Color { int red; int green; @@ -1766,7 +1766,7 @@ making it do the following: create an `IntegerTriple` with the values in order. * Otherwise, throw an `Error`. -```dart:run-dartpad:height-415px:ga_id-factory_constructors +```dartpad class IntegerHolder { IntegerHolder(); @@ -1937,7 +1937,7 @@ default constructor with zeros as the arguments. Ignore all initial errors in the DartPad. -```dart:run-dartpad:height-255px:ga_id-redirecting_constructors +```dartpad class Color { int red; int green; @@ -2024,7 +2024,7 @@ and create a constant constructor that does the following: Ignore all initial errors in the DartPad. -```dart:run-dartpad:ga_id-const_constructors +```dartpad class Recipe { List ingredients; int calories; diff --git a/src/content/codelabs/iterables.md b/src/content/codelabs/iterables.md index 38ce720983..6fea696c31 100644 --- a/src/content/codelabs/iterables.md +++ b/src/content/codelabs/iterables.md @@ -126,7 +126,7 @@ using a `for-in` loop. The following example shows you how to read elements using a `for-in` loop. -```dart:run-dartpad:ga_id-for_in_loop +```dartpad void main() { const iterable = ['Salad', 'Popcorn', 'Toast']; for (final element in iterable) { @@ -173,7 +173,7 @@ results in a [StateError.][StateError class] ::: -```dart:run-dartpad:ga_id-first_and_last +```dartpad void main() { Iterable iterable = const ['Salad', 'Popcorn', 'Toast']; print('The first element is ${iterable.first}'); @@ -211,7 +211,7 @@ Run the following example to see how `firstWhere()` works. Do you think all the functions will give the same result? -```dart:run-dartpad:height-565px:ga_id-using_firstwhere +```dartpad bool predicate(String item) { return item.length > 5; } @@ -313,7 +313,7 @@ satisfies the following conditions: All the elements in the test data are [strings][String class]; you can check the class documentation for help. -```dart:run-dartpad:theme-dark:ga_id-practice_writing_a_test_predicate +```dartpad theme="dark" // Implement the predicate of singleWhere // with the following conditions // * The element contains the character `'a'` @@ -421,7 +421,7 @@ you can use to verify conditions: Run this exercise to see them in action. -```dart:run-dartpad:height-255px:ga_id-using_any_and_every +```dartpad void main() { const items = ['Salad', 'Popcorn', 'Toast']; @@ -470,7 +470,7 @@ Use `any()` and `every()` to implement two functions: * Part 2: Implement `everyUserOver13()`. * Return `true` if all users are 14 or older. -```dart:run-dartpad:theme-dark:height-395px:ga_id-verify_iterable +```dartpad bool anyUserUnder18(Iterable users) { // TODO: Implement the anyUserUnder18 function. } @@ -660,7 +660,7 @@ Run this example to see how `where()` can be used together with other methods like `any()`. -```dart:run-dartpad:height-380px:ga_id-using_where +```dartpad void main() { var evenNumbers = const [1, -2, 3, 42].where((number) => number.isEven); @@ -703,7 +703,7 @@ Run this example to see how `takeWhile()` and `skipWhile()` can split an `Iterable` containing numbers. -```dart:run-dartpad:ga_id-using_takewhile +```dartpad void main() { const numbers = [1, 3, -2, 0, 4, 5]; @@ -747,7 +747,7 @@ Use `where()` to implement two functions: * Return an `Iterable` containing all users with names of length 3 or less. -```dart:run-dartpad:theme-dark:height-380px:ga_id-filtering_elements_from_a_list +```dartpad theme="dark" Iterable filterOutUnder21(Iterable users) { // TODO: Implement the filterOutUnder21 function. } @@ -893,7 +893,7 @@ multiply all the elements of an `Iterable` by 2. What do you think the output will be? -```dart:run-dartpad:ga_id-using_map +```dartpad void main() { var numbersByTwo = const [1, -2, 3, 42].map((number) => number * 2); print('Numbers: $numbersByTwo'); @@ -913,7 +913,7 @@ contains strings containing each user's name and age. Each string in the `Iterable` must follow this format: `'{name} is {age}'`—for example `'Alice is 21'`. -```dart:run-dartpad:theme-dark:height-310px:ga_id-mapping_to_a_different_type +```dartpad theme="dark" Iterable getNameAndAges(Iterable users) { // TODO: Implement the getNameAndAges function. } @@ -1043,7 +1043,7 @@ Part 3: Implement `validEmailAddresses()`. - Use the provided function `isValidEmailAddress()` to evaluate whether an `EmailAddress` is valid. -```dart:run-dartpad:theme-dark:height-600px:ga_id-putting_it_all_together +```dartpad theme="dark" Iterable parseEmailAddresses(Iterable strings) { // TODO: Implement the parseEmailAddresses function. } diff --git a/src/content/language/callable-objects.md b/src/content/language/callable-objects.md index 005d306f77..be42a5a2ad 100644 --- a/src/content/language/callable-objects.md +++ b/src/content/language/callable-objects.md @@ -25,7 +25,7 @@ that takes three strings and concatenates them, separating each with a space, and appending an exclamation. Click **Run** to execute the code. -```dart:run-dartpad:height-350px:ga_id-callable_objects +```dartpad class WannabeFunction { String call(String a, String b, String c) => '$a $b $c!'; } diff --git a/src/content/language/constructors.md b/src/content/language/constructors.md index e435cea71e..67225df453 100644 --- a/src/content/language/constructors.md +++ b/src/content/language/constructors.md @@ -430,7 +430,7 @@ The following example initializes three `final` fields in an initializer list. To execute the code, click **Run**. -```dart:run-dartpad:height-340px:ga_id-initializer_list +```dartpad import 'dart:math'; class Point { @@ -484,7 +484,7 @@ the `Employee` class constructor calls the named constructor for its superclass, `Person`. To execute the following code, click **Run**. -```dart:run-dartpad:height-450px:ga_id-non_default_superclass_constructor +```dartpad class Person { String? firstName; diff --git a/src/content/language/functions.md b/src/content/language/functions.md index 2cc109ac97..768e5ebe91 100644 --- a/src/content/language/functions.md +++ b/src/content/language/functions.md @@ -309,7 +309,7 @@ for (var item in uppercaseList) { Click **Run** to execute the code. -```dart:run-dartpad:height-400px:ga_id-anonymous_functions +```dartpad void main() { const list = ['apples', 'bananas', 'oranges']; diff --git a/src/content/libraries/async/using-streams.md b/src/content/libraries/async/using-streams.md index 02dec1d4fd..2a139450d0 100644 --- a/src/content/libraries/async/using-streams.md +++ b/src/content/libraries/async/using-streams.md @@ -62,7 +62,7 @@ This page uses embedded DartPads to display runnable examples. ::: -```dart:run-dartpad +```dartpad Future sumStream(Stream stream) async { var sum = 0; await for (final value in stream) { @@ -113,7 +113,7 @@ error using **try-catch**. The following example throws an error when the loop iterator equals 4: -```dart:run-dartpad +```dartpad Future sumStream(Stream stream) async { var sum = 0; try { diff --git a/src/content/overview.md b/src/content/overview.md index 69caa553a3..9b158ce79d 100644 --- a/src/content/overview.md +++ b/src/content/overview.md @@ -59,7 +59,7 @@ To learn more about the language, check out the [Dart language tour](/language). -```dart:run-dartpad:ga_id-overview +```dartpad import 'dart:math' show Random; void main() async { diff --git a/src/content/tutorials/server/fetch-data.md b/src/content/tutorials/server/fetch-data.md index 634a95cd41..c762145527 100644 --- a/src/content/tutorials/server/fetch-data.md +++ b/src/content/tutorials/server/fetch-data.md @@ -530,7 +530,7 @@ that requests, decodes, then displays the mock information about the `http` and `path` packages: -```dart:run-dartpad:height-600px:ga_id-fetch-data-complete +```dartpad import 'dart:convert'; import 'package:http/http.dart' as http; diff --git a/src/content/tutorials/server/get-started.md b/src/content/tutorials/server/get-started.md index be59d15f02..c07d833fdb 100644 --- a/src/content/tutorials/server/get-started.md +++ b/src/content/tutorials/server/get-started.md @@ -31,7 +31,7 @@ greeting to use another language. ::: -```dart:run-dartpad:ga_id-hello_world +```dartpad void main() { print('Hello, World!'); }